conan: windows initial support

This commit is contained in:
Yaroslav Gurov
2026-03-21 02:03:29 +01:00
parent b94cddcb1c
commit 2f01aac483
14 changed files with 497 additions and 29 deletions

View File

@@ -4,7 +4,7 @@ set(PROJECT AmneziaVPN)
set(AMNEZIAVPN_VERSION 4.8.13.1)
include(cmake/conan_bootstrap.cmake)
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES ${CMAKE_SOURCE_DIR}/cmake/conan_provider.cmake)
set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES ${CMAKE_SOURCE_DIR}/cmake/conan_provider.cmake CACHE STRING "" FORCE)
if(APPLE)
get_property(generator_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)

View File

@@ -296,7 +296,7 @@ QString Utils::certUtilPath()
QString Utils::tun2socksPath()
{
#ifdef Q_OS_WIN
return Utils::executable("xray/tun2socks", true);
return Utils::executable("tun2socks", true);
#elif defined Q_OS_LINUX
// We have service that runs OpenVPN on Linux. We need to make same
// path for client and service.

View File

@@ -19,7 +19,7 @@ class AmneziaVPN(ConanFile):
if has_service:
if os == "Windows":
self.requires("awg-windows/1.0.0")
self.requires("awg-windows/0.1.8")
else:
self.requires("awg-go/0.2.16")
@@ -39,4 +39,4 @@ class AmneziaVPN(ConanFile):
self.requires("openvpn-pt-android/1.0.0")
self.requires("libssh/0.11.3@amnezia")
self.requires("openssl/3.5.5")
self.requires("openssl/3.6.1")

View File

@@ -1,6 +1,6 @@
# conanfile.py
from conan import ConanFile
from conan.tools.files import get, copy, collect_libs, chdir
from conan.tools.files import get, copy, collect_libs, chdir, rename
from conan.tools.layout import basic_layout
from conan.errors import ConanInvalidConfiguration
from conan.tools.gnu import Autotools, AutotoolsToolchain
@@ -11,7 +11,6 @@ import os
class AmneziaXrayBindings(ConanFile):
name = "amnezia-xray-bindings"
version = "1.1.0"
settings = "os", "arch"
@property
@@ -35,6 +34,9 @@ class AmneziaXrayBindings(ConanFile):
def _is_windows(self):
return str(self.settings.os).startswith("Windows")
def config_options(self):
self.package_type = "shared-library" if self._is_windows else "static-library"
def validate(self):
if not self._goos or not self._goarch:
raise ConanInvalidConfiguration(
@@ -46,6 +48,11 @@ class AmneziaXrayBindings(ConanFile):
def build_requirements(self):
self.tool_requires("go/1.26.0")
if self._is_windows:
self.win_bash = True
if not self.conf.get("tools.microsoft.bash:path", check_type=str):
self.tool_requires("msys2/cci.latest")
self.tool_requires("mingw-builds/15.1.0")
def source(self):
get(self, "https://github.com/amnezia-vpn/amnezia-xray-bindings/archive/v1.1.0.zip",
@@ -53,6 +60,9 @@ class AmneziaXrayBindings(ConanFile):
def generate(self):
tc = AutotoolsToolchain(self)
tc.make_args = [
"LIB_ARC=libamnezia_xray.a"
]
env = Environment()
env.define("ARCH", self._goarch)
env.define("GOARCH", self._goarch)
@@ -68,6 +78,11 @@ class AmneziaXrayBindings(ConanFile):
autotools = Autotools(self)
autotools.make()
def _rename_header(self):
if not self._is_windows:
rename(self, os.path.join(self.package_folder, "include", "libamnezia_xray.h"),
os.path.join(self.package_folder, "include", "amnezia_xray.h"))
def _rename_libs(self):
# workaround of bad naming strategy in amnezia-xray-bindings
# TODO: change it and kick out the code below
@@ -81,9 +96,9 @@ class AmneziaXrayBindings(ConanFile):
def package(self):
copy(self, "*.h", src=self.build_folder, dst=os.path.join(self.package_folder, "include"))
copy(self, "*.a", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
copy(self, "*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
copy(self, "*.lib", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
self._rename_libs()
copy(self, "*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "bin"))
self._rename_header()
def package_info(self):
self.cpp_info.set_property("cmake_target_name", "amnezia::xray-bindings")

View File

@@ -0,0 +1,103 @@
from conan import ConanFile
from conan.tools.layout import basic_layout
from conan.errors import ConanInvalidConfiguration
from conan.tools.files import get, copy, chdir, collect_libs
from conan.tools.gnu import AutotoolsToolchain
import os
class AwgWindows(ConanFile):
name = "awg-windows"
version = "0.1.8"
settings = "os", "arch"
@property
def _goarm(self):
return {
"armv5el": "5",
"armv5hf": "5",
"armv6": "6",
"armv7": "7",
"armv7hf": "7",
"armv7s": "7",
"armv7k": "7",
}.get(str(self.settings.arch))
@property
def _goarch(self):
return {
"x86": "386",
"x86_64": "amd64",
"armv5el": "arm",
"armv5hf": "arm",
"armv6": "arm",
"armv7": "arm",
"armv7hf": "arm",
"armv7s": "arm",
"armv7k": "arm",
"armv8": "arm64",
"armv8_32": "arm64",
"armv8.3": "arm64",
"arm64ec": "arm64"
}.get(str(self.settings.arch))
def layout(self):
basic_layout(self)
def validate(self):
if not str(self.settings.os).startswith("Windows"):
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} is to be used on Windows only!"
)
if not self._goarch:
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} does not support {self.settings.arch} architecture"
)
def build_requirements(self):
self.tool_requires("mingw-builds/15.1.0")
self.tool_requires("go/1.26.0")
def requirements(self):
self.requires("wintun/[*]")
def source(self):
get(self, f"https://github.com/amnezia-vpn/amneziawg-windows/archive/refs/tags/v{self.version}.zip",
sha256="1de472832b332515c96cdf14ea887edde42ed7ad173675280c51baa9a3ef62f2", strip_root=True)
def generate(self):
tc = AutotoolsToolchain(self)
tc.extra_cflags = [
"-Wall",
"-Wno-unused-function",
"-Wno-switch",
"-DWINVER=0x0601"
]
tc.extra_ldflags = [
"-Wl,--dynamicbase",
"-Wl,--nxcompat",
"-Wl,--export-all-symbols",
"-Wl,--high-entropy-va"
]
env = tc.environment()
env.define("GOOS", "windows")
if self._goarm:
env.define("GOARM", self._goarm)
env.define("GOARCH", self._goarch)
env.define("CGO_ENABLED", "1")
env.define("CGO_LDFLAGS", tc.ldflags)
env.define("CGO_CFLAGS", tc.cflags)
tc.generate(env)
def build(self):
with chdir(self, self.source_folder):
self.run(f'go build -buildmode c-shared -ldflags="-w -s" -trimpath -v -o "{os.path.join(self.build_folder, "tunnel.dll")}"')
def package(self):
copy(self, "*.h", src=self.build_folder, dst=os.path.join(self.package_folder, "include"))
copy(self, "*.dll", src=self.build_folder, dst=os.path.join(self.package_folder, "lib"))
def package_info(self):
self.cpp_info.set_property("cmake_target_name", "amnezia::awg-windows")
self.cpp_info.libs = collect_libs(self)

View File

@@ -1,4 +1,5 @@
from conan import ConanFile
from conan.tools.layout import basic_layout
from conan.tools.files import get, copy
import os
@@ -8,12 +9,15 @@ class Golang(ConanFile):
version = "1.26.0"
settings = "os", "arch"
def layout(self):
basic_layout(self)
def build(self):
get(self, **self.conan_data["sources"][str(self.version)][str(self.settings.os)][str(self.settings.arch)])
get(self, **self.conan_data["sources"][str(self.version)][str(self.settings.os)][str(self.settings.arch)], strip_root=True, destination="go")
def package(self):
copy(self, "*", src=os.path.join(self.source_folder, "go"), dst=self.package_folder)
copy(self, "*", src=os.path.join(self.build_folder, "go"), dst=self.package_folder)
def package_info(self):
self.cpp_info.bindirs = ["bin"]

View File

@@ -1,7 +1,8 @@
from conan import ConanFile
from conan.tools.files import get, copy
from conan.tools.gnu import Autotools, AutotoolsToolchain, AutotoolsDeps
from conan.tools.files import get, copy, replace_in_file
from conan.tools.gnu import Autotools, AutotoolsToolchain, AutotoolsDeps, PkgConfigDeps
from conan.tools.layout import basic_layout
from conan.tools.cmake import cmake_layout, CMakeToolchain, CMake, CMakeDeps
import os
@@ -9,40 +10,89 @@ class Openvpn(ConanFile):
name = "openvpn"
version = "2.7.0"
package_type = "application"
settings = "os", "build_type", "arch"
settings = "os", "build_type", "arch", "compiler"
@property
def _is_windows(self):
return str(self.settings.os).startswith("Windows")
def export_sources(self):
copy(self, "*applink.c", src=self.recipe_folder, dst=self.export_sources_folder)
def layout(self):
basic_layout(self)
if self._is_windows:
cmake_layout(self)
else:
basic_layout(self)
def build_requirements(self):
self.tool_requires("libtool/2.4.7")
self.tool_requires("automake/1.16.5")
if self._is_windows:
self.tool_requires("cmake/[>=3.14 <4]")
self.tool_requires("pkgconf/2.5.1")
else:
self.tool_requires("libtool/2.4.7")
self.tool_requires("automake/1.16.5")
def requirements(self):
self.requires("openssl/[>=1.1.0]", visible=False)
self.requires("lz4/[>=1.7.1]", visible=False)
self.requires("lzo/2.10", visible=False)
if self._is_windows:
self.requires("tap-windows6/[*]")
def source(self):
get(self, f"https://github.com/OpenVPN/openvpn/archive/refs/tags/v{self.version}.zip",
sha256="1a65d8587f932c13d55b1f175ff2e1d61d795d9092788662e888054854d4ee3d", strip_root=True
)
def _patch_sources(self):
replace_in_file(self,
os.path.join(self.source_folder, "CMakeLists.txt"),
"/Qspectre",
""
)
def generate(self):
tc = AutotoolsToolchain(self)
tc.generate()
deps = AutotoolsDeps(self)
deps.generate()
self._patch_sources()
if self._is_windows:
tc = CMakeToolchain(self)
applink_include_path = os.path.join(self.export_sources_folder, "include").replace("\\", "/")
tap_include_path = (self.dependencies["tap-windows6"].cpp_info.aggregated_components().includedirs[0]).replace("\\", "/")
tc.extra_cflags = [ f"-I{tap_include_path}", f"-I{applink_include_path}" ]
tc.extra_cxxflags = [ f"-I{tap_include_path}", f"-I{applink_include_path}" ]
tc.cache_variables["BUILD_TESTING"] = False
tc.cache_variables["ENABLE_PKCS11"] = False
tc.generate()
deps = CMakeDeps(self)
deps.generate()
pkgconf = PkgConfigDeps(self)
pkgconf.generate()
else:
tc = AutotoolsToolchain(self)
tc.generate()
deps = AutotoolsDeps(self)
deps.generate()
def build(self):
at = Autotools(self)
at.autoreconf()
at.configure()
at.make()
if self._is_windows:
cmake = CMake(self)
cmake.configure()
cmake.build()
else:
at = Autotools(self)
at.autoreconf()
at.configure()
at.make()
def package(self):
copy(self, "openvpn", src=os.path.join(self.build_folder, "src", "openvpn"), dst=self.package_folder)
if self._is_windows:
copy(self, "*openvpn.exe", src=self.build_folder, dst=self.package_folder, keep_path=False)
else:
copy(self, "openvpn", src=os.path.join(self.build_folder, "src", "openvpn"), dst=self.package_folder)
def package_info(self):
self.cpp_info.exe = True
self.cpp_info.location = os.path.join(self.package_folder, "openvpn")
ext = ".exe" if self._is_windows else ""
self.cpp_info.location = os.path.join(self.package_folder, f"openvpn{ext}")

View File

@@ -0,0 +1,151 @@
/*
* Copyright 2004-2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#define APPLINK_STDIN 1
#define APPLINK_STDOUT 2
#define APPLINK_STDERR 3
#define APPLINK_FPRINTF 4
#define APPLINK_FGETS 5
#define APPLINK_FREAD 6
#define APPLINK_FWRITE 7
#define APPLINK_FSETMOD 8
#define APPLINK_FEOF 9
#define APPLINK_FCLOSE 10 /* should not be used */
#define APPLINK_FOPEN 11 /* solely for completeness */
#define APPLINK_FSEEK 12
#define APPLINK_FTELL 13
#define APPLINK_FFLUSH 14
#define APPLINK_FERROR 15
#define APPLINK_CLEARERR 16
#define APPLINK_FILENO 17 /* to be used with below */
#define APPLINK_OPEN 18 /* formally can't be used, as flags can vary */
#define APPLINK_READ 19
#define APPLINK_WRITE 20
#define APPLINK_LSEEK 21
#define APPLINK_CLOSE 22
#define APPLINK_MAX 22 /* always same as last macro */
#ifndef APPMACROS_ONLY
/*
* Normally, do not define APPLINK_NO_INCLUDES. Define it if you are using
* symbol preprocessing and do not want the preprocessing to affect the
* following included header files. You will need to put these
* include lines somewhere in the file that is including applink.c.
*/
#ifndef APPLINK_NO_INCLUDES
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#endif
#ifdef __BORLANDC__
/* _lseek in <io.h> is a function-like macro so we can't take its address */
#undef _lseek
#define _lseek lseek
#endif
static void *app_stdin(void)
{
return stdin;
}
static void *app_stdout(void)
{
return stdout;
}
static void *app_stderr(void)
{
return stderr;
}
static int app_feof(FILE *fp)
{
return feof(fp);
}
static int app_ferror(FILE *fp)
{
return ferror(fp);
}
static void app_clearerr(FILE *fp)
{
clearerr(fp);
}
static int app_fileno(FILE *fp)
{
return _fileno(fp);
}
static int app_fsetmod(FILE *fp, char mod)
{
return _setmode(_fileno(fp), mod == 'b' ? _O_BINARY : _O_TEXT);
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void **
#if defined(__BORLANDC__)
/*
* __stdcall appears to be the only way to get the name
* decoration right with Borland C. Otherwise it works
* purely incidentally, as we pass no parameters.
*/
__stdcall
#else
__cdecl
#endif
OPENSSL_Applink(void)
{
static int once = 1;
static void *OPENSSL_ApplinkTable[APPLINK_MAX + 1] = { (void *)APPLINK_MAX };
if (once) {
OPENSSL_ApplinkTable[APPLINK_STDIN] = app_stdin;
OPENSSL_ApplinkTable[APPLINK_STDOUT] = app_stdout;
OPENSSL_ApplinkTable[APPLINK_STDERR] = app_stderr;
OPENSSL_ApplinkTable[APPLINK_FPRINTF] = fprintf;
OPENSSL_ApplinkTable[APPLINK_FGETS] = fgets;
OPENSSL_ApplinkTable[APPLINK_FREAD] = fread;
OPENSSL_ApplinkTable[APPLINK_FWRITE] = fwrite;
OPENSSL_ApplinkTable[APPLINK_FSETMOD] = app_fsetmod;
OPENSSL_ApplinkTable[APPLINK_FEOF] = app_feof;
OPENSSL_ApplinkTable[APPLINK_FCLOSE] = fclose;
OPENSSL_ApplinkTable[APPLINK_FOPEN] = fopen;
OPENSSL_ApplinkTable[APPLINK_FSEEK] = fseek;
OPENSSL_ApplinkTable[APPLINK_FTELL] = ftell;
OPENSSL_ApplinkTable[APPLINK_FFLUSH] = fflush;
OPENSSL_ApplinkTable[APPLINK_FERROR] = app_ferror;
OPENSSL_ApplinkTable[APPLINK_CLEARERR] = app_clearerr;
OPENSSL_ApplinkTable[APPLINK_FILENO] = app_fileno;
OPENSSL_ApplinkTable[APPLINK_OPEN] = _open;
OPENSSL_ApplinkTable[APPLINK_READ] = _read;
OPENSSL_ApplinkTable[APPLINK_WRITE] = _write;
OPENSSL_ApplinkTable[APPLINK_LSEEK] = _lseek;
OPENSSL_ApplinkTable[APPLINK_CLOSE] = _close;
once = 0;
}
return OPENSSL_ApplinkTable;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,44 @@
from conan import ConanFile
from conan.tools.layout import basic_layout
from conan.errors import ConanInvalidConfiguration
from conan.tools.files import get, copy
import os
class TapWindows6(ConanFile):
name = "tap-windows6"
version = "9.27.0"
settings = "os", "arch"
@property
def _arch(self):
return {
"x86": "i386",
"x86_64": "amd64",
"armv8": "arm64"
}.get(str(self.settings.arch))
def layout(self):
basic_layout(self)
def validate(self):
if not str(self.settings.os).startswith("Windows"):
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} does only support Windows"
)
if not self._arch:
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} does not support {self.settings.arch}"
)
def source(self):
get(self, f"https://github.com/OpenVPN/tap-windows6/releases/download/{self.version}/dist.win10.zip",
sha256="36e2609b7ceefedcb978ce5c48caf9e0e5af83423717c4e2e3c1d7ebca8f62a5", strip_root=True)
def package(self):
copy(self, "*.h", src=os.path.join(self.source_folder, "include"), dst=os.path.join(self.package_folder, "include"))
copy(self, "*", src=os.path.join(self.source_folder, self._arch), dst=os.path.join(self.package_folder, "bin"))
def package_info(self):
self.cpp_info.set_property("cmake_target_name", "openvpn::tap-windows6")

View File

@@ -48,6 +48,15 @@ class Tun2Socks(ConanFile):
def build_requirements(self):
self.tool_requires("go/1.26.0")
if self._is_windows:
self.win_bash = True
if not self.conf.get("tools.microsoft.bash:path", check_type=str):
self.tool_requires("msys2/cci.latest")
self.tool_requires("mingw-builds/15.1.0")
def requirements(self):
if self._is_windows:
self.requires("wintun/[*]")
def source(self):
get(self, f"https://github.com/xjasonlyu/tun2socks/archive/refs/tags/v{self.version}.zip",

View File

@@ -24,6 +24,6 @@ class V2rayRulesDat(ConanFile):
def package_info(self):
self.cpp_info.set_property("cmake_target_name", "Loyalsoldier::v2ray-rules-dat")
self.cpp_info.set_property("cmake_extra_variables", {
"GEOSITE_DAT_PATH": os.path.join(self.package_folder, "geosite.dat"),
"GEOIP_DAT_PATH": os.path.join(self.package_folder, "geoip.dat")
"GEOSITE_DAT_PATH": os.path.join(self.package_folder, "geosite.dat").replace("\\", "/"),
"GEOIP_DAT_PATH": os.path.join(self.package_folder, "geoip.dat").replace("\\", "/")
})

View File

@@ -0,0 +1,44 @@
from conan import ConanFile
from conan.tools.layout import basic_layout
from conan.tools.files import download, copy
from conan.errors import ConanInvalidConfiguration
import os
class WinSplitTunnel(ConanFile):
name = "win-split-tunnel"
version = "1.2.5.0"
settings = "os", "build_type", "compiler"
@property
def _arch(self):
return {
"x86_64": "x86_64",
"armv8": "aarch64"
}.get(str(self.settings.arch))
@property
def _target(self):
return f"{self._arch}-pc-windows-msvc"
def layout(self):
basic_layout(self)
def validate(self):
if not str(self.settings.os).startswith("Windows"):
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} supports only Windows"
)
def source(self):
url = f"https://raw.githubusercontent.com/mullvad/mullvadvpn-app-binaries/ff0e3746c89a04314377cffeb52faaa976413a69/{self._target}/split-tunnel"
download(self, url, "mullvad-split-tunnel.cat", sha256="")
download(self, url, "mullvad-split-tunnel.inf", sha256="")
download(self, url, "mullvad-split-tunnel.pdb", sha256="")
download(self, url, "mullvad-split-tunnel.sys", sha256="")
def package(self):
copy(self, "*", src=self.source_folder, dst=os.path.join(self.package_folder, "bin"))
def package_info(self):
self.cpp_info.set_property("cmake_target_name", "mullvad::win-split-tunnel")

View File

@@ -1,5 +1,45 @@
from conan import ConanFile
from conan.tools.layout import basic_layout
from conan.tools.files import get, copy
from conan.errors import ConanInvalidConfiguration
import os
class PackageConan(ConanFile):
name = "wintun"
version = "1.0.0"
version = "0.14.1"
settings = "os", "arch"
@property
def _arch(self):
return {
"x86_64": "amd64",
"armv8": "arm64",
"x86": "x86"
}.get(str(self.settings.arch))
def layout(self):
basic_layout(self)
def validate(self):
if not str(self.settings.os).startswith("Windows"):
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} is supposed to be run on Windows only"
)
if not self._arch:
raise ConanInvalidConfiguration(
f"{self.name} v{self.version} does not support {self.settings.arch}"
)
def source(self):
get(self, f"https://www.wintun.net/builds/wintun-{self.version}.zip",
sha256="07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51", strip_root=True)
def package(self):
copy(self, "*.dll", src=os.path.join(self.source_folder, "bin", self._arch), dst=os.path.join(self.package_folder, "bin"))
copy(self, "*.h", src=os.path.join(self.source_folder, "include"), dst=os.path.join(self.package_folder, "include"))
def package_info(self):
self.cpp_info.set_property("cmake_target_name", "zx2c4::wintun")
self.cpp_info.libs = [ "wintun" ]

View File

@@ -393,3 +393,11 @@ add_custom_command(TARGET ${PROJECT} POST_BUILD
${GEOIP_DAT_PATH}
$<TARGET_FILE_DIR:${PROJECT}>
)
if (WIN32)
add_custom_command(TARGET ${PROJECT} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:zx2c4::wintun>
$<TARGET_FILE_DIR:${PROJECT}>
)
endif()