├── .gitignore ├── etc ├── default │ └── forwarder ├── forwarder.conf └── init.d │ └── forwarder ├── .gitmodules ├── src ├── Address.hpp ├── CMakeLists.txt ├── Settings.hpp ├── Listener.cpp ├── Listener.hpp ├── Client.hpp ├── Settings.cpp ├── Client.cpp └── Main.cpp ├── README.md ├── 3rdparty └── CMakeLists.txt ├── CMakeLists.txt ├── Jenkinsfile ├── .clang-format ├── LICENSE └── CDeploy /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /.vscode 3 | 4 | /build 5 | -------------------------------------------------------------------------------- /etc/default/forwarder: -------------------------------------------------------------------------------- 1 | # Additional options that are passed to the daemon. 2 | DAEMON_OPTS= 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rdparty/libnstd"] 2 | path = 3rdparty/libnstd 3 | url = ../libnstd.git 4 | -------------------------------------------------------------------------------- /etc/forwarder.conf: -------------------------------------------------------------------------------- 1 | 2 | # A list of listen and target addresses. 3 | # Example: 4 | #forward 0.0.0.0:80:192.168.0.27:8080 5 | #forward 443:example-site.com:443 6 | # 7 | # The syntax is: 8 | #forward [:]:: 9 | -------------------------------------------------------------------------------- /src/Address.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | 6 | struct Address 7 | { 8 | uint32 addr; 9 | uint16 port; 10 | 11 | Address() 12 | : addr(Socket::anyAddress) 13 | , port(0) 14 | { 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(sources 3 | Address.hpp 4 | Main.cpp 5 | Settings.cpp 6 | Settings.hpp 7 | Client.cpp 8 | Client.hpp 9 | Listener.cpp 10 | Listener.hpp 11 | ) 12 | 13 | add_executable(forwarder 14 | ${sources} 15 | ) 16 | 17 | target_link_libraries(forwarder PRIVATE 18 | libnstd::Socket 19 | ) 20 | 21 | source_group("" FILES ${sources}) 22 | 23 | set_property(TARGET forwarder PROPERTY FOLDER "src") 24 | 25 | install(TARGETS forwarder DESTINATION usr/sbin) 26 | 27 | -------------------------------------------------------------------------------- /src/Settings.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "Address.hpp" 8 | 9 | struct Settings 10 | { 11 | struct Tunnel 12 | { 13 | Address listenAddr; 14 | String targetHost; 15 | uint16 targetPort; 16 | 17 | Tunnel() 18 | : targetPort() 19 | { 20 | } 21 | }; 22 | 23 | List tunnels; 24 | 25 | Settings(); 26 | 27 | static void loadSettings(const String& file, Settings& settings); 28 | }; 29 | -------------------------------------------------------------------------------- /src/Listener.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Listener.hpp" 3 | 4 | #include 5 | 6 | Server::Client::ICallback* Listener::onAccepted(Server::Client& client_, uint32 ip, uint16 port) 7 | { 8 | Log::debugf("%s: Accepted client for %s:%hu", (const char*)Socket::inetNtoA(ip), 9 | (const char*)_destinationHost, _destinationPort); 10 | 11 | Address addr; 12 | addr.addr = ip; 13 | addr.port = port; 14 | Client& client = _clients.append(_server, client_, addr, *this); 15 | if (!client.connect(_destinationHost, _destinationPort)) 16 | { 17 | _clients.remove(client); 18 | return nullptr; 19 | } 20 | 21 | return &client; 22 | } 23 | 24 | void Listener::onClosed(Client& client) 25 | { 26 | _clients.remove(client); 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Forwarder 3 | 4 | [![Build Status](http://xaws6t1emwa2m5pr.myfritz.net:8080/buildStatus/icon?job=craflin%2Fforwarder%2Fmaster)](http://xaws6t1emwa2m5pr.myfritz.net:8080/job/craflin/job/forwarder/job/master/) 5 | 6 | A very simple TCP v4 port forwarder daemon. 7 | 8 | The daemon accepts TCP connections on configured ports and forwards them to another address. 9 | 10 | ## Build Instructions 11 | 12 | * Clone the repository and initialize submodules. 13 | * Build the project with `cmake`. 14 | * You can build a `deb` package using the target `package` in CMake. 15 | 16 | ## Setup 17 | 18 | * Install `forwarder` from the `deb` package. 19 | * Configure TCP tunnels in `/etc/forwarder.conf`. 20 | * Start the `forwarder` daemon with `sudo systemctl start forwarder`. 21 | * You can use `sudo systemctl enable forwarder` to start the daemon automatically after a system restart. 22 | -------------------------------------------------------------------------------- /src/Listener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Client.hpp" 5 | 6 | #include 7 | #include 8 | 9 | class Listener : public Server::Listener::ICallback 10 | , public Client::ICallback 11 | { 12 | public: 13 | Listener(Server& server, const String& destinationHost, uint16 destinationPort) 14 | : _server(server) 15 | , _destinationHost(destinationHost) 16 | , _destinationPort(destinationPort) 17 | { 18 | } 19 | 20 | public: // Server::Listener::ICallback 21 | Server::Client::ICallback* onAccepted(Server::Client& client, uint32 ip, uint16 port) override; 22 | 23 | public: // Client::ICallback 24 | void onClosed(Client& client) override; 25 | 26 | private: 27 | Server& _server; 28 | const String _destinationHost; 29 | const uint16 _destinationPort; 30 | PoolList _clients; 31 | }; 32 | -------------------------------------------------------------------------------- /3rdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | set(DISABLE_ADD_TEST True) 4 | function(add_test) 5 | if(NOT DISABLE_ADD_TEST) 6 | _add_test(${ARGV}) 7 | endif() 8 | endfunction() 9 | 10 | set(DISABLE_INSTALL True) 11 | function(install) 12 | if(NOT DISABLE_INSTALL) 13 | _install(${ARGV}) 14 | endif() 15 | endfunction() 16 | 17 | set(DISABLE_ENABLE_TESTING True) 18 | function(enable_testing) 19 | if(NOT DISABLE_ENABLE_TESTING) 20 | _enable_testing(${ARGV}) 21 | endif() 22 | endfunction() 23 | 24 | set(DISABLE_ADD_SUBDIRECTORY True) 25 | function(add_subdirectory) 26 | if(NOT DISABLE_ADD_SUBDIRECTORY OR "${ARGV0}" STREQUAL "libnstd" OR "${ARGV0}" STREQUAL "src" OR "${ARGV0}" STREQUAL "Socket") 27 | _add_subdirectory(${ARGV}) 28 | endif() 29 | endfunction() 30 | 31 | add_subdirectory(libnstd) 32 | 33 | set_property(TARGET nstd nstdSocket PROPERTY FOLDER "3rdparty") 34 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.1) 3 | cmake_policy(SET CMP0048 NEW) 4 | 5 | project(forwarder VERSION 0.2.0) 6 | 7 | set(CDEPLOY_NO_DEBUG_BUILD True) 8 | set(CDEPLOY_NO_COMPILER True) 9 | 10 | include(CDeploy) 11 | 12 | add_subdirectory(3rdparty) 13 | add_subdirectory(src) 14 | 15 | set(CPACK_GENERATOR "DEB") 16 | set(CPACK_PACKAGE_CONTACT "Colin Graf ") 17 | set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") 18 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A very simple TCP v4 port forwarder daemon") 19 | set(CPACK_PACKAGING_INSTALL_PREFIX "/") 20 | #set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT") 21 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/craflin/forwarder") 22 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.15)") 23 | set(CPACK_DEBIAN_PACKAGE_SECTION "net") 24 | 25 | include(CPack) 26 | 27 | install(FILES etc/forwarder.conf DESTINATION etc) 28 | install(FILES etc/default/forwarder DESTINATION etc/default) 29 | install(PROGRAMS etc/init.d/forwarder DESTINATION etc/init.d) 30 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent none 3 | stages { 4 | stage('All') { 5 | matrix { 6 | agent { 7 | label "${platform}" 8 | } 9 | axes { 10 | axis { 11 | name 'platform' 12 | values 'ubuntu22.04-x86_64', 'ubuntu20.04-x86_64', 'ubuntu18.04-x86_64', 'raspbian10-armv7l' 13 | } 14 | } 15 | stages { 16 | stage('Build') { 17 | steps { 18 | cmakeBuild buildDir: 'build', cleanBuild: true, installation: 'InSearchPath', buildType: 'Release', cmakeArgs: '-G Ninja' 19 | cmake workingDir: 'build', arguments: '--build . --target package', installation: 'InSearchPath' 20 | archiveArtifacts artifacts: 'build/*.deb' 21 | } 22 | } 23 | } 24 | } 25 | } 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /etc/init.d/forwarder: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | ### BEGIN INIT INFO 4 | # Provides: forwarder 5 | # Required-Start: $network 6 | # Required-Stop: $network 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: A very simple TCP v4 port forwarder daemon 10 | # Description: The daemon accepts TCP connections on configured ports and 11 | # forwards them to another address. 12 | ### END INIT INFO 13 | # 14 | # DAEMON Location of the binary 15 | # 16 | 17 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 18 | DAEMON=/usr/sbin/forwarder 19 | NAME=forwarder 20 | DESC="Forwarder daemon" 21 | DAEMON_OPTS= 22 | 23 | test -x $DAEMON || exit 0 24 | 25 | # Include custom values if available 26 | if [ -f /etc/default/forwarder ] ; then 27 | . /etc/default/forwarder 28 | fi 29 | 30 | DAEMON_OPTS="-b $DAEMON_OPTS" 31 | 32 | start() { 33 | echo -n "Starting $DESC: " 34 | 35 | $DAEMON $DAEMON_OPTS 36 | if [ $? -eq 0 ]; then 37 | echo "$NAME." 38 | else 39 | echo "failed!" 40 | fi 41 | } 42 | 43 | stop() { 44 | echo -n "Stopping $DESC: " 45 | kill $(pidof $DAEMON) 46 | if [ $? -eq 0 ]; then 47 | echo "$NAME." 48 | else 49 | echo "failed!" 50 | fi 51 | } 52 | 53 | case "$1" in 54 | start) 55 | start 56 | ;; 57 | stop) 58 | stop 59 | ;; 60 | restart|reload|force-reload) 61 | stop 62 | start 63 | ;; 64 | *) 65 | echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2 66 | exit 2 67 | ;; 68 | esac 69 | 70 | exit 0 71 | -------------------------------------------------------------------------------- /src/Client.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "Address.hpp" 5 | 6 | #include 7 | 8 | class Client : public Server::Client::ICallback 9 | , public Server::Establisher::ICallback 10 | { 11 | public: 12 | class ICallback 13 | { 14 | public: 15 | virtual void onClosed(Client& client) = 0; 16 | 17 | protected: 18 | ICallback() { } 19 | ~ICallback() { } 20 | }; 21 | 22 | public: 23 | Client(Server& server, Server::Client& client, const Address& address, ICallback& callback); 24 | ~Client(); 25 | 26 | bool connect(const String& host, uint16 port); 27 | 28 | public: // Server::Client::ICallback 29 | void onRead() override { forward(_client, *_uplink); } 30 | void onWrite() override { _uplink->resume(); } 31 | void onClosed() override { _callback.onClosed(*this); } 32 | 33 | public: // Server::Establisher::ICallback 34 | Server::Client::ICallback* onConnected(Server::Client& client) override; 35 | void onAbolished() override; 36 | 37 | private: 38 | class Uplink : public Server::Client::ICallback 39 | { 40 | public: 41 | Uplink(Client& client) 42 | : _client(client) 43 | { 44 | } 45 | 46 | public: // Server::Client::ICallback 47 | void onRead() override { forward(*_client._uplink, _client._client); } 48 | void onWrite() override { _client._client.resume(); } 49 | void onClosed() override { _client._callback.onClosed(_client); } 50 | 51 | private: 52 | Client& _client; 53 | }; 54 | 55 | private: 56 | Server& _server; 57 | Server::Client& _client; 58 | const Address _address; 59 | ICallback& _callback; 60 | String _destinationHost; 61 | uint16 _destinationPort; 62 | Server::Establisher* _establisher; 63 | Uplink _uplinkCallback; 64 | Server::Client* _uplink; 65 | 66 | private: 67 | static void forward(Server::Client& from, Server::Client& to); 68 | }; 69 | -------------------------------------------------------------------------------- /src/Settings.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Settings.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | Settings::Settings() { } 9 | 10 | void Settings::loadSettings(const String& file, Settings& settings) 11 | { 12 | String conf; 13 | if (!File::readAll(file, conf)) 14 | return; 15 | List lines; 16 | conf.split(lines, "\n\r"); 17 | for (List::Iterator i = lines.begin(), end = lines.end(); i != end; ++i) 18 | { 19 | String line = *i; 20 | const char* lineEnd = line.find('#'); 21 | if (lineEnd) 22 | line.resize(lineEnd - (const char*)line); 23 | line.trim(); 24 | if (line.isEmpty()) 25 | continue; 26 | List tokens; 27 | line.split(tokens, " \t"); 28 | if (tokens.size() < 2) 29 | continue; 30 | const String& option = *tokens.begin(); 31 | const String& value = *(++tokens.begin()); 32 | if (option == "forward") 33 | { 34 | List tokens; 35 | value.split(tokens, ":", false); 36 | if (tokens.size() < 3) 37 | Log::warningf("Invalid forwarder arguments: %s", (const char*)value); 38 | else 39 | { 40 | Settings::Tunnel tunnel; 41 | List::Iterator args = tokens.begin(); 42 | if (tokens.size() >= 4) 43 | { 44 | tunnel.listenAddr.addr = Socket::inetAddr(*args); 45 | ++args; 46 | } 47 | tunnel.listenAddr.port = args->toUInt(); 48 | ++args; 49 | tunnel.targetHost = *args; 50 | ++args; 51 | tunnel.targetPort = args->toUInt(); 52 | settings.tunnels.append(tunnel); 53 | } 54 | } 55 | else 56 | Log::warningf("Unknown option: %s", (const char*)option); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Client.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Client.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | Client::Client(Server& server, Server::Client& client, const Address& address, ICallback& callback) 9 | : _server(server) 10 | , _client(client) 11 | , _address(address) 12 | , _callback(callback) 13 | , _destinationPort(0) 14 | , _establisher(nullptr) 15 | , _uplinkCallback(*this) 16 | , _uplink(nullptr) 17 | { 18 | client.suspend(); 19 | } 20 | 21 | Client::~Client() 22 | { 23 | _server.remove(_client); 24 | if (_establisher) 25 | _server.remove(*_establisher); 26 | if (_uplink) 27 | _server.remove(*_uplink); 28 | } 29 | 30 | bool Client::connect(const String& host, uint16 port) 31 | { 32 | _destinationHost = host; 33 | _destinationPort = port; 34 | _establisher = _server.connect(host, port, *this); 35 | if (!_establisher) 36 | return false; 37 | return true; 38 | } 39 | 40 | void Client::forward(Server::Client& from, Server::Client& to) 41 | { 42 | usize size; 43 | byte buffer[262144]; 44 | if (!from.read(buffer, sizeof(buffer), size)) 45 | return; 46 | usize postponed; 47 | if (!to.write(buffer, size, &postponed)) 48 | return; 49 | if (postponed) 50 | from.suspend(); 51 | } 52 | 53 | Server::Client::ICallback* Client::onConnected(Server::Client& client) 54 | { 55 | Log::infof("%s: Established connection with %s:%hu", (const char*)Socket::inetNtoA(_address.addr), 56 | (const char*)_destinationHost, _destinationPort); 57 | 58 | _client.resume(); 59 | _uplink = &client; 60 | return &_uplinkCallback; 61 | } 62 | 63 | void Client::onAbolished() 64 | { 65 | Log::infof("%s: Failed to establish connection with %s:%hu: %s", (const char*)Socket::inetNtoA(_address.addr), 66 | (const char*)_destinationHost, _destinationPort, (const char*)Error::getErrorString()); 67 | 68 | _callback.onClosed(*this); 69 | } 70 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Settings.hpp" 8 | #include "Listener.hpp" 9 | 10 | class Main 11 | { 12 | public: 13 | Main(const Settings& settings) 14 | : _settings(settings) 15 | { 16 | _server.setReuseAddress(true); 17 | _server.setKeepAlive(true); 18 | _server.setNoDelay(true); 19 | } 20 | 21 | bool start(Address& failedAddress) 22 | { 23 | for (List::Iterator i = _settings.tunnels.begin(), end = _settings.tunnels.end(); i != end; ++i) 24 | { 25 | const Settings::Tunnel& tunnel = *i; 26 | Listener& listener = _listeners.append(_server, tunnel.targetHost, tunnel.targetPort); 27 | if (!_server.listen(tunnel.listenAddr.addr, tunnel.listenAddr.port, listener)) 28 | { 29 | failedAddress = tunnel.listenAddr; 30 | return false; 31 | } 32 | } 33 | return true; 34 | } 35 | 36 | void run() { _server.run(); } 37 | 38 | private: 39 | const Settings& _settings; 40 | Server _server; 41 | PoolList _listeners; 42 | }; 43 | 44 | int main(int argc, char* argv[]) 45 | { 46 | String logFile; 47 | String configFile("/etc/forwarder.conf"); 48 | 49 | // parse parameters 50 | { 51 | Process::Option options[] = { 52 | { 'b', "daemon", Process::argumentFlag | Process::optionalFlag }, 53 | { 'c', "config", Process::argumentFlag }, 54 | { 'h', "help", Process::optionFlag }, 55 | }; 56 | Process::Arguments arguments(argc, argv, options); 57 | int character; 58 | String argument; 59 | while (arguments.read(character, argument)) 60 | switch (character) 61 | { 62 | case 'b': 63 | logFile = argument.isEmpty() ? String("/dev/null") : argument; 64 | break; 65 | case 'c': 66 | configFile = argument; 67 | break; 68 | case '?': 69 | Console::errorf("Unknown option: %s.\n", (const char*)argument); 70 | return -1; 71 | case ':': 72 | Console::errorf("Option %s required an argument.\n", (const char*)argument); 73 | return -1; 74 | default: 75 | Console::errorf("Usage: %s [-b] [-c ]\n\ 76 | \n\ 77 | -b, --daemon[=]\n\ 78 | Detach from calling shell and write output to .\n\ 79 | \n\ 80 | -c , --config[=]\n\ 81 | Load configuration from . (Default is /etc/forwarder.conf)\n\ 82 | \n", 83 | argv[0]); 84 | return -1; 85 | } 86 | } 87 | 88 | Log::setLevel(Log::debug); 89 | 90 | // load settings 91 | Settings settings; 92 | Settings::loadSettings(configFile, settings); 93 | 94 | // daemonize process 95 | #ifndef _WIN32 96 | if (!logFile.isEmpty()) 97 | { 98 | Log::infof("Starting as daemon..."); 99 | if (!Process::daemonize(logFile)) 100 | { 101 | Log::errorf("Could not daemonize process: %s", (const char*)Error::getErrorString()); 102 | return -1; 103 | } 104 | Log::setDevice(Log::syslog); 105 | Log::setLevel(Log::info); 106 | } 107 | #endif 108 | // start the server 109 | Main main(settings); 110 | { 111 | Address failedAddress; 112 | if (!main.start(failedAddress)) 113 | return Log::errorf("Could not listen on TCP port %s:%hu: %s", (const char*)Socket::inetNtoA(failedAddress.addr), (uint16)failedAddress.port, (const char*)Socket::getErrorString()), 1; 114 | } 115 | for (List::Iterator i = settings.tunnels.begin(), end = settings.tunnels.end(); i != end; ++i) 116 | Log::infof("Listening on TCP port %hu...", (uint16)i->listenAddr.port); 117 | 118 | // run the server 119 | main.run(); 120 | return 1; 121 | } 122 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: WebKit 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: DontAlign 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: false 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortBlocksOnASingleLine: Empty 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: All 18 | AllowShortLambdasOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: MultiLine 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: false 29 | AfterClass: true 30 | AfterControlStatement: true 31 | AfterEnum: true 32 | AfterFunction: true 33 | AfterNamespace: false 34 | AfterObjCDeclaration: true 35 | AfterStruct: true 36 | AfterUnion: true 37 | AfterExternBlock: false 38 | BeforeCatch: true 39 | BeforeElse: true 40 | IndentBraces: false 41 | SplitEmptyFunction: true 42 | SplitEmptyRecord: true 43 | SplitEmptyNamespace: true 44 | BreakBeforeBinaryOperators: All 45 | BreakBeforeBraces: Custom 46 | BreakBeforeInheritanceComma: true 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeComma 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 0 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: false 60 | DeriveLineEnding: true 61 | DerivePointerAlignment: false 62 | DisableFormat: false 63 | ExperimentalAutoDetectBinPacking: false 64 | FixNamespaceComments: false 65 | ForEachMacros: 66 | - foreach 67 | - Q_FOREACH 68 | - BOOST_FOREACH 69 | IncludeBlocks: Preserve 70 | IncludeCategories: 71 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 72 | Priority: 2 73 | SortPriority: 0 74 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 75 | Priority: 3 76 | SortPriority: 0 77 | - Regex: '.*' 78 | Priority: 1 79 | SortPriority: 0 80 | IncludeIsMainRegex: '(Test)?$' 81 | IncludeIsMainSourceRegex: '' 82 | IndentCaseLabels: false 83 | IndentGotoLabels: true 84 | IndentPPDirectives: None 85 | IndentWidth: 4 86 | IndentWrappedFunctionNames: false 87 | JavaScriptQuotes: Leave 88 | JavaScriptWrapImports: true 89 | KeepEmptyLinesAtTheStartOfBlocks: true 90 | MacroBlockBegin: '' 91 | MacroBlockEnd: '' 92 | MaxEmptyLinesToKeep: 1 93 | NamespaceIndentation: None 94 | ObjCBinPackProtocolList: Auto 95 | ObjCBlockIndentWidth: 4 96 | ObjCSpaceAfterProperty: true 97 | ObjCSpaceBeforeProtocolList: true 98 | PenaltyBreakAssignment: 2 99 | PenaltyBreakBeforeFirstCallParameter: 19 100 | PenaltyBreakComment: 300 101 | PenaltyBreakFirstLessLess: 120 102 | PenaltyBreakString: 1000 103 | PenaltyBreakTemplateDeclaration: 10 104 | PenaltyExcessCharacter: 1000000 105 | PenaltyReturnTypeOnItsOwnLine: 60 106 | PointerAlignment: Left 107 | ReflowComments: true 108 | SortIncludes: true 109 | SortUsingDeclarations: true 110 | SpaceAfterCStyleCast: false 111 | SpaceAfterLogicalNot: false 112 | SpaceAfterTemplateKeyword: true 113 | SpaceBeforeAssignmentOperators: true 114 | SpaceBeforeCpp11BracedList: true 115 | SpaceBeforeCtorInitializerColon: true 116 | SpaceBeforeInheritanceColon: true 117 | SpaceBeforeParens: ControlStatements 118 | SpaceBeforeRangeBasedForLoopColon: true 119 | SpaceInEmptyBlock: true 120 | SpaceInEmptyParentheses: false 121 | SpacesBeforeTrailingComments: 1 122 | SpacesInAngles: false 123 | SpacesInConditionalStatement: false 124 | SpacesInContainerLiterals: true 125 | SpacesInCStyleCastParentheses: false 126 | SpacesInParentheses: false 127 | SpacesInSquareBrackets: false 128 | SpaceBeforeSquareBrackets: false 129 | Standard: Latest 130 | StatementMacros: 131 | - Q_UNUSED 132 | - QT_REQUIRE_VERSION 133 | TabWidth: 8 134 | UseCRLF: false 135 | UseTab: Never 136 | ... 137 | 138 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /CDeploy: -------------------------------------------------------------------------------- 1 | 2 | macro(deploy_package package version) 3 | 4 | include(CMakeParseArguments) 5 | 6 | cmake_parse_arguments(_deploy_package "NO_OS;NO_ARCH;NO_COMPILER;NO_CACHE;NO_CONFIG_MAPPING" "" "COMPONENTS" ${ARGN}) 7 | 8 | if(MSVC) 9 | if(_deploy_package_NO_CONFIG_MAPPING) 10 | foreach(_deploy_package_config ${CMAKE_CONFIGURATION_TYPES}) 11 | if(NOT "${_deploy_package_config}" STREQUAL "Debug") 12 | string(TOUPPER "${_deploy_package_config}" _deploy_package_config) 13 | unset(CMAKE_MAP_IMPORTED_CONFIG_${_deploy_package_config}) 14 | endif() 15 | endforeach() 16 | else() 17 | foreach(_deploy_package_config ${CMAKE_CONFIGURATION_TYPES}) 18 | if(NOT "${_deploy_package_config}" STREQUAL "Debug") 19 | string(TOUPPER "${_deploy_package_config}" _deploy_package_config) 20 | set(CMAKE_MAP_IMPORTED_CONFIG_${_deploy_package_config} Release) 21 | endif() 22 | endforeach() 23 | endif() 24 | endif() 25 | 26 | set(_deploy_package_find_package_args) 27 | if(_deploy_package_COMPONENTS) 28 | list(APPEND _deploy_package_find_package_args COMPONENTS ${_deploy_package_COMPONENTS}) 29 | endif() 30 | 31 | if("${version}" MATCHES "[^\\-]+-.+") 32 | string(REGEX REPLACE "([^\\-]+)-.+" "\\1" _deploy_package_find_version "${version}") 33 | else() 34 | set(_deploy_package_find_version "${version}") 35 | endif() 36 | 37 | if(NOT _deploy_package_NO_CACHE) 38 | find_package(${package} ${_deploy_package_find_version} EXACT QUIET CONFIG ${_deploy_package_find_package_args} NO_DEFAULT_PATH) 39 | endif() 40 | 41 | if(NOT ${package}_FOUND) 42 | 43 | function(_deploy_package_download) 44 | 45 | foreach(component ${_deploy_package_COMPONENTS}) 46 | set(${package}${component}_DIR "${package}${component}_DIR-NOTFOUND" PARENT_SCOPE) 47 | endforeach() 48 | 49 | string(TOLOWER "${package}" filename) 50 | set(filename "${filename}-${version}") 51 | if(NOT _deploy_package_NO_OS) 52 | set(filename "${filename}-${CDEPLOY_OS}") 53 | endif() 54 | if(NOT _deploy_package_NO_ARCH) 55 | set(filename "${filename}-${CDEPLOY_ARCH}") 56 | endif() 57 | if(NOT _deploy_package_NO_COMPILER) 58 | set(filename "${filename}-${CDEPLOY_COMPILER}") 59 | endif() 60 | set(filename "${filename}.zip") 61 | 62 | if(NOT CDEPLOY_CACHE_DIR) 63 | set(CDEPLOY_CACHE_DIR "$ENV{CDEPLOY_CACHE_DIR}") 64 | if(NOT CDEPLOY_CACHE_DIR) 65 | if(WIN32) 66 | set(CDEPLOY_CACHE_DIR "$ENV{USERPROFILE}/.cmake/downloadcache") 67 | else() 68 | set(CDEPLOY_CACHE_DIR "$ENV{HOME}") 69 | if(NOT CDEPLOY_CACHE_DIR OR "${CDEPLOY_CACHE_DIR}" STREQUAL "/") 70 | set(CDEPLOY_CACHE_DIR "/tmp") 71 | else() 72 | set(CDEPLOY_CACHE_DIR "${CDEPLOY_CACHE_DIR}/.cmake/downloadcache") 73 | endif() 74 | endif() 75 | endif() 76 | endif() 77 | 78 | set(cache_file "${CDEPLOY_CACHE_DIR}/${filename}") 79 | if(NOT _deploy_package_NO_CACHE AND EXISTS "${cache_file}") 80 | message("-- Found ${filename} in cache") 81 | else() 82 | 83 | set(repository "${_deploy_package_UNPARSED_ARGUMENTS}") 84 | if(NOT repository) 85 | if(CDEPLOY_REPOSITORY) 86 | set(repository "${CDEPLOY_REPOSITORY}") 87 | else() 88 | set(repository "$ENV{CDEPLOY_REPOSITORY}") 89 | endif() 90 | endif() 91 | 92 | set(url "${repository}/${filename}") 93 | message("-- Downloading ${url}") 94 | file(DOWNLOAD "${url}" "${cache_file}.part" STATUS download_status) 95 | list(GET download_status 0 _download_result) 96 | if(NOT ${_download_result} EQUAL 0) 97 | list(GET download_status 1 _download_error) 98 | message(FATAL_ERROR "Could not download: ${_download_error} (${_download_result})") 99 | return() 100 | endif() 101 | file(RENAME "${cache_file}.part" "${cache_file}") 102 | endif() 103 | 104 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar tf "${cache_file}" 105 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" 106 | OUTPUT_VARIABLE _unzip_output 107 | RESULT_VARIABLE _unzip_result) 108 | if(NOT ${_unzip_result} EQUAL 0) 109 | message(FATAL_ERROR "Could not extract file") 110 | return() 111 | endif() 112 | string(REGEX REPLACE "([^/]+).*" "\\1" _extract_dir "${_unzip_output}") 113 | 114 | execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${cache_file}" 115 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" 116 | RESULT_VARIABLE _unzip_result) 117 | if(NOT ${_unzip_result} EQUAL 0) 118 | message(FATAL_ERROR "Could not extract file") 119 | return() 120 | endif() 121 | 122 | set(_deploy_package_package_folder "${CMAKE_BINARY_DIR}/${_extract_dir}" PARENT_SCOPE) 123 | endfunction() 124 | 125 | _deploy_package_download() 126 | 127 | find_package(${package} ${_deploy_package_find_version} EXACT QUIET CONFIG REQUIRED 128 | ${_deploy_package_find_package_args} 129 | PATHS "${_deploy_package_package_folder}" NO_DEFAULT_PATH 130 | ) 131 | 132 | endif() 133 | endmacro() 134 | 135 | function(get_target_arch var) 136 | set(${var} ${CMAKE_SYSTEM_PROCESSOR}) 137 | if(WIN32) 138 | if(MSVC) 139 | if(CMAKE_CL_64) 140 | set(${var} x64) 141 | else() 142 | set(${var} x86) 143 | endif() 144 | elseif(CMAKE_COMPILER_IS_GNUCC) # CMAKE_CXX_COMPILER_ARCHITECTURE_ID does not provide anything with MinGW compilers 145 | execute_process(COMMAND "${CMAKE_CXX_COMPILER}" -v OUTPUT_VARIABLE GCC_VERSION_OUTPUT ERROR_VARIABLE GCC_VERSION_OUTPUT) 146 | if(GCC_VERSION_OUTPUT MATCHES ".*x86_64.*") 147 | set(${var} x64) 148 | else() 149 | set(${var} x86) 150 | endif() 151 | endif() 152 | endif() 153 | set(${var} ${${var}} PARENT_SCOPE) 154 | endfunction() 155 | 156 | function(get_target_compiler var) 157 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 158 | string(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gcc_version "${CMAKE_CXX_COMPILER_VERSION}") 159 | if(NOT gcc_version VERSION_LESS "5.0") 160 | string(REGEX REPLACE "([0-9]+).*" "\\1" gcc_version "${gcc_version}") 161 | endif() 162 | set(${var} "gcc${gcc_version}") 163 | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 164 | string(REGEX REPLACE "([0-9]+).*" "clang\\1" ${var} "${CMAKE_CXX_COMPILER_VERSION}") 165 | elseif(MSVC) 166 | set(MSVC_YEARS 2008 2010 2012 2013 2015 2017 2019) 167 | set(MSVC_MSC_VERS 150 160 170 180 190 191 192) 168 | string(SUBSTRING "${MSVC_VERSION}" 0 3 MSVC_VER_SHORT) 169 | list(FIND MSVC_MSC_VERS ${MSVC_VER_SHORT} MSVC_INDEX) 170 | list(GET MSVC_YEARS ${MSVC_INDEX} MSVS_YEAR) 171 | set(${var} vs${MSVS_YEAR}) 172 | else() 173 | string(TOLOWER "${CMAKE_CXX_COMPILER_ID}${CMAKE_CXX_COMPILER_VERSION}" ${var}) 174 | endif() 175 | set(${var} ${${var}} PARENT_SCOPE) 176 | endfunction() 177 | 178 | function(get_target_os var) 179 | if(WIN32) 180 | set(${var} windows) 181 | elseif(APPLE) 182 | set(${var} macos) 183 | else() 184 | file(GLOB ETC_RELEASE_FILES /etc/*-release) 185 | list(REMOVE_ITEM ETC_RELEASE_FILES /etc/lsb-release) 186 | list(GET ETC_RELEASE_FILES 0 ETC_RELEASE_FILE) 187 | file(READ "${ETC_RELEASE_FILE}" ETC_RELEASE_FILE_CONTENT) 188 | string(REGEX REPLACE "=|\n" ";" ETC_RELEASE_FILE_CONTENT "${ETC_RELEASE_FILE_CONTENT}") 189 | string(REGEX REPLACE "; +| +;" ";" ETC_RELEASE_FILE_CONTENT "${ETC_RELEASE_FILE_CONTENT}") 190 | list(FIND ETC_RELEASE_FILE_CONTENT "NAME" NAME_INDEX) 191 | if(NAME_INDEX GREATER -1) 192 | math(EXPR NAME_INDEX "${NAME_INDEX} + 1") 193 | list(GET ETC_RELEASE_FILE_CONTENT "${NAME_INDEX}" ETC_RELEASE_NAME) 194 | else() 195 | list(GET ETC_RELEASE_FILE_CONTENT 0 ETC_RELEASE_NAME) 196 | endif() 197 | string(REGEX MATCH "[^ \"]+" ETC_RELEASE_NAME "${ETC_RELEASE_NAME}") 198 | list(FIND ETC_RELEASE_FILE_CONTENT "VERSION_ID" VERSION_INDEX) 199 | if(NOT VERSION_INDEX GREATER -1) 200 | list(FIND ETC_RELEASE_FILE_CONTENT "VERSION" VERSION_INDEX) 201 | endif() 202 | if(VERSION_INDEX GREATER -1) 203 | math(EXPR VERSION_INDEX "${VERSION_INDEX} + 1") 204 | list(GET ETC_RELEASE_FILE_CONTENT "${VERSION_INDEX}" ETC_RELEASE_VERSION) 205 | string(REGEX MATCH "[^ \"]+" ETC_RELEASE_VERSION "${ETC_RELEASE_VERSION}") 206 | else() 207 | string(REGEX MATCH "[0-9]+" ETC_RELEASE_VERSION "${ETC_RELEASE_FILE_CONTENT}") 208 | endif() 209 | string(TOLOWER "${ETC_RELEASE_NAME}${ETC_RELEASE_VERSION}" ${var}) 210 | endif() 211 | set(${var} ${${var}} PARENT_SCOPE) 212 | endfunction() 213 | 214 | get_target_arch(CDEPLOY_ARCH) 215 | get_target_compiler(CDEPLOY_COMPILER) 216 | get_target_os(CDEPLOY_OS) 217 | 218 | set(CPACK_GENERATOR "ZIP") 219 | string(TOLOWER "${PROJECT_NAME}" CPACK_PACKAGE_FILE_NAME) 220 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${PROJECT_VERSION}") 221 | if(CDEPLOY_PACKAGE_REVISION) 222 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_PACKAGE_REVISION}") 223 | endif() 224 | if(NOT CDEPLOY_NO_OS) 225 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_OS}") 226 | endif() 227 | if(NOT CDEPLOY_NO_ARCH) 228 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_ARCH}") 229 | endif() 230 | if(NOT CDEPLOY_NO_COMPILER) 231 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}-${CDEPLOY_COMPILER}") 232 | endif() 233 | 234 | if(MSVC AND NOT CDEPLOY_NO_DEBUG_BUILD) 235 | 236 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 237 | set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER ".cmake") 238 | 239 | include(ExternalProject) 240 | 241 | ExternalProject_Add(DEBUG_BUILD 242 | SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" 243 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build-debug" 244 | CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/install-debug" -DCDEPLOY_NO_DEBUG_BUILD=True -DCMAKE_BUILD_TYPE=Debug 245 | BUILD_COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/build-debug" --config Debug 246 | INSTALL_COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/build-debug" --config Debug --target install 247 | BUILD_ALWAYS True 248 | ) 249 | file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/install-debug") 250 | install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/install-debug/" DESTINATION . USE_SOURCE_PERMISSIONS) 251 | 252 | set_property(TARGET DEBUG_BUILD PROPERTY FOLDER ".cmake") 253 | if(NOT CDEPLOY_DEBUG_BUILD) 254 | set_property(TARGET DEBUG_BUILD PROPERTY EXCLUDE_FROM_DEFAULT_BUILD True) 255 | set_property(TARGET DEBUG_BUILD PROPERTY EXCLUDE_FROM_ALL True) 256 | endif() 257 | 258 | endif() 259 | if(CDEPLOY_DEBUG_BUILD) 260 | endif() 261 | 262 | if(MSVC) 263 | set(CMAKE_DEBUG_POSTFIX d) 264 | endif() 265 | 266 | file(REMOVE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in") 267 | 268 | function(deploy_export_init) 269 | 270 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in") 271 | file(WRITE "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "@PACKAGE_INIT@\n\n") 272 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "set_and_check(INSTALL_DIR \"@PACKAGE_INSTALL_DIR@\")\n\n") 273 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "include(CMakeFindDependencyMacro)\n\n") 274 | endif() 275 | 276 | endfunction() 277 | 278 | function(deploy_export_dependency name) 279 | deploy_export_init() 280 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "find_dependency(${name} ${ARGN})\n\n") 281 | endfunction() 282 | 283 | function(deploy_export name) 284 | 285 | include(CMakeParseArguments) 286 | 287 | set(options INTERFACE LIBRARY STATIC SHARED EXECUTABLE) 288 | set(oneValueArgs CONFIGURATION IMPORTED_LOCATION IMPORTED_IMPLIB) 289 | set(multiValueArgs INTERFACE_INCLUDE_DIRECTORIES INTERFACE_SOURCES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES PROPERTIES) 290 | cmake_parse_arguments(_ "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 291 | 292 | deploy_export_init() 293 | 294 | set(target_flags) 295 | if(__STATIC) 296 | set(target_flags "${target_flags} STATIC") 297 | endif() 298 | if(__SHARED) 299 | set(target_flags "${target_flags} SHARED") 300 | endif() 301 | if(__INTERFACE) 302 | set(target_flags "${target_flags} INTERFACE") 303 | endif() 304 | 305 | set(target_config) 306 | if(__CONFIGURATION) 307 | string(TOUPPER "${__CONFIGURATION}" __CONFIGURATION) 308 | set(target_config "_${__CONFIGURATION}") 309 | endif() 310 | 311 | set(target_properties) 312 | if(__IMPORTED_LOCATION) 313 | set(target_properties "${target_properties} IMPORTED_LOCATION${target_config} \"\${INSTALL_DIR}/${__IMPORTED_LOCATION}\"") 314 | endif() 315 | if(__IMPORTED_IMPLIB) 316 | set(target_properties "${target_properties} IMPORTED_IMPLIB${target_config} \"\${INSTALL_DIR}/${__IMPORTED_IMPLIB}\"") 317 | endif() 318 | if(__INTERFACE_INCLUDE_DIRECTORIES) 319 | set(target_properties "${target_properties} INTERFACE_INCLUDE_DIRECTORIES \"") 320 | foreach(dir ${__INTERFACE_INCLUDE_DIRECTORIES}) 321 | set(target_properties "${target_properties}\${INSTALL_DIR}/${dir};") 322 | endforeach() 323 | set(target_properties "${target_properties}\"") 324 | endif() 325 | if(__INTERFACE_SOURCES) 326 | set(target_properties "${target_properties} INTERFACE_SOURCES \"") 327 | foreach(file ${__INTERFACE_SOURCES}) 328 | set(target_properties "${target_properties}\${INSTALL_DIR}/${file};") 329 | endforeach() 330 | set(target_properties "${target_properties}\"") 331 | endif() 332 | foreach(property INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_FEATURES INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) 333 | if(__${property}) 334 | set(target_properties "${target_properties} ${property} \"") 335 | foreach(arg ${__${property}}) 336 | set(target_properties "${target_properties}${arg};") 337 | endforeach() 338 | set(target_properties "${target_properties}\"") 339 | endif() 340 | endforeach() 341 | if(__PROPERTIES) 342 | foreach(arg ${__PROPERTIES}) 343 | set(target_properties "${target_properties} ${arg}") 344 | endforeach() 345 | endif() 346 | 347 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "if(NOT TARGET ${PROJECT_NAME}::${name})\n") 348 | if(__LIBRARY OR __INTERFACE) 349 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " add_library(${PROJECT_NAME}::${name} ${target_flags} IMPORTED GLOBAL)\n") 350 | else() 351 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " add_executable(${PROJECT_NAME}::${name} IMPORTED GLOBAL)\n") 352 | endif() 353 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "endif()\n") 354 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "set_target_properties(${PROJECT_NAME}::${name}\n") 355 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " PROPERTIES\n") 356 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" " ${target_properties}\n") 357 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" ")\n") 358 | if(__CONFIGURATION) 359 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "set_property(TARGET ${PROJECT_NAME}::${name} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${__CONFIGURATION})\n") 360 | endif() 361 | file(APPEND "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "\n") 362 | 363 | endfunction() 364 | 365 | function(install_deploy_export) 366 | include(CMakePackageConfigHelpers) 367 | if(EXISTS "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in") 368 | set(INSTALL_DIR .) 369 | configure_package_config_file("${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake.in" "${PROJECT_NAME}Config.cmake" 370 | INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}" 371 | PATH_VARS 372 | INSTALL_DIR 373 | ) 374 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake" DESTINATION "lib/cmake/${PROJECT_NAME}") 375 | else() 376 | install(EXPORT ${PROJECT_NAME}Config 377 | DESTINATION "lib/cmake/${PROJECT_NAME}" 378 | NAMESPACE ${PROJECT_NAME}:: 379 | ) 380 | endif() 381 | write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" COMPATIBILITY ExactVersion) 382 | install(FILES "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION "lib/cmake/${PROJECT_NAME}") 383 | endfunction() 384 | 385 | --------------------------------------------------------------------------------