diff --git a/Assets/NSM2/NetworkServiceManager.cs b/Assets/NSM2/NetworkManager.cs similarity index 98% rename from Assets/NSM2/NetworkServiceManager.cs rename to Assets/NSM2/NetworkManager.cs index 9da57f5..86d9675 100644 --- a/Assets/NSM2/NetworkServiceManager.cs +++ b/Assets/NSM2/NetworkManager.cs @@ -56,7 +56,7 @@ public enum NetworkEvent } - public class NetworkServiceManager : Singleton + public class NetworkManager : Singleton { IntPtr _service = IntPtr.Zero; IntPtr[] _sessions = new IntPtr[1]; @@ -327,7 +327,7 @@ static void HandleNativeConsolePrint(int level, string msg) [MonoPInvokeCallback(typeof(YASIO_NI.YNIEventDelegate))] static void HandleNativeNetworkIoEvent(ref YASIO_NI.IOEvent ev) { - var nsm = NetworkServiceManager.Instance; + var nsm = NetworkManager.Instance; Debug.LogFormat("The channel connect_id={0}, bytes_transferred={1}", YASIO_NI.yasio_connect_id(nsm._service, ev.channel), YASIO_NI.yasio_bytes_transferred(nsm._service, ev.channel)); switch ((YASIO_NI.YEnums)ev.kind) diff --git a/Assets/NSM2/NetworkServiceManager.cs.meta b/Assets/NSM2/NetworkManager.cs.meta similarity index 100% rename from Assets/NSM2/NetworkServiceManager.cs.meta rename to Assets/NSM2/NetworkManager.cs.meta diff --git a/Assets/Scripts/SampleNetworkPacketHandler.cs b/Assets/Scripts/SampleNetworkPacketHandler.cs index cb92647..b91c89b 100644 --- a/Assets/Scripts/SampleNetworkPacketHandler.cs +++ b/Assets/Scripts/SampleNetworkPacketHandler.cs @@ -139,7 +139,7 @@ public void HandleEvent(NetworkEvent ev, int cmd, Span ud, int channel) reply.uid = msg.uid; reply.status = 200; // 200 表示success Span udReply = reply.encode(); - NetworkServiceManager.Instance.SendSerializedMsg(AppProtocol.CMD_LOGIN_RESP, udReply, AppProtocol.SERVER_CHANNEL); + NetworkManager.Instance.SendSerializedMsg(AppProtocol.CMD_LOGIN_RESP, udReply, AppProtocol.SERVER_CHANNEL); } else if(cmd == AppProtocol.CMD_LOGIN_RESP) { // SampelScene应该是 channel:0 收到 diff --git a/Assets/Scripts/SampleScene.cs b/Assets/Scripts/SampleScene.cs index c949093..626a749 100644 --- a/Assets/Scripts/SampleScene.cs +++ b/Assets/Scripts/SampleScene.cs @@ -31,13 +31,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE public class SampleScene : MonoBehaviour, NetworkEventListener { - NetworkServiceManager nsm; + NetworkManager nsm; // Start is called before the first frame update void Start() { Debug.Log("Start Game!"); - nsm = NetworkServiceManager.Instance; + nsm = NetworkManager.Instance; nsm.Start(AppProtocol.MAX_CHANNELS, new SampleNetworkPacketHandler()); nsm.ListenAt("127.0.0.1", AppProtocol.PORT, AppProtocol.SERVER_CHANNEL); // 启动本地TCP服务 nsm.AddEventListener(this); diff --git a/NativeLibs/yasio/1k/1kiss.ps1 b/NativeLibs/yasio/1k/1kiss.ps1 new file mode 100644 index 0000000..77e9e98 --- /dev/null +++ b/NativeLibs/yasio/1k/1kiss.ps1 @@ -0,0 +1,1992 @@ +# ////////////////////////////////////////////////////////////////////////////////////////// +# // A multi-platform support c++11 library with focus on asynchronous socket I/O for any +# // client application. +# ////////////////////////////////////////////////////////////////////////////////////////// +# +# The MIT License (MIT) +# +# Copyright (c) 2012-2024 HALX99 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# The 1k/1kiss.ps1, the core script of project 1kiss(1k) +# options +# -p: build target platform: win32,winrt(winuwp),linux,android,osx(mac),ios,tvos,watchos,wasm +# for android: will search ndk in sdk_root which is specified by env:ANDROID_HOME first, +# if not found, by default will install ndk-r16b or can be specified by option: -cc 'ndk-r23c' +# -a: build arch: x86,x64,armv7,arm64 +# -d: the build workspace, i.e project root which contains root CMakeLists.txt, empty use script run working directory aka cwd +# -cc: The C/C++ compiler toolchain: clang, msvc, gcc(mingw) or empty use default installed on current OS +# msvc: msvc-120, msvc-141 +# ndk: ndk-r16b, ndk-r16b+ +# -xt: cross build tool, default: cmake, for android can be gradlew, can be path of cross build tool program +# -xc: cross build tool configure options: i.e. -xc '-Dbuild' +# -xb: cross build tool build options: i.e. -xb '--config','Release' +# -prefix: the install location for missing tools in system, default is "$HOME/.1kiss" +# -sdk: specific windows sdk version, i.e. -sdk '10.0.19041.0', leave empty, cmake will auto choose latest avaiable +# -setupOnly: this param present, only execute step: setup +# -configOnly: if this param present, will skip build step +# support matrix +# | OS | Build targets | C/C++ compiler toolchain | Cross Build tool | +# +----------+----------------------+---------------------------+------------------| +# | Windows | win32,winrt | msvc,clang,gcc(mingw) | cmake | +# | Linux | linux,android | gcc,ndk | cmake,gradle | +# | macOS | osx,ios,tvos,watchos | xcode | cmake | +# android gradle, there a two props: +# - -P__1K_CMAKE_VERSION +# - -P__1k_ARCHS +# startsWith('__1K') indicate 1k gradle props +# startsWith('_1K') indicate it's a cmake config option +# +param( + [switch]$configOnly, + [switch]$setupOnly, + [switch]$forceConfig, + [switch]$ndkOnly, + [switch]$rebuild +) + +$myRoot = $PSScriptRoot + +$HOST_WIN = 0 # targets: win,uwp,android +$HOST_LINUX = 1 # targets: linux,android +$HOST_MAC = 2 # targets: android,ios,osx(macos),tvos,watchos + +# 0: windows, 1: linux, 2: macos +$Global:IsWin = $IsWindows -or ("$env:OS" -eq 'Windows_NT') +if ($Global:IsWin) { $HOST_OS = $HOST_WIN } +else { + if ($IsLinux) { $HOST_OS = $HOST_LINUX } + elseif ($IsMacOS) { $HOST_OS = $HOST_MAC } + else { + throw "Unsupported host OS to run 1k/1kiss.ps1" + } +} +$Global:ENV_PATH_SEP = @(':', ';')[$IsWin] +$Global:EXE_SUFFIX = @('', '.exe')[$IsWin] + +$Script:cmake_generator = $null + +# import VersionEx +. (Join-Path $PSScriptRoot 'extensions.ps1') + +class _1kiss { + [void] println($msg) { + Write-Host "1kiss: $msg" + } + + [void] print($msg) { + Write-Host "1kiss: $msg" -NoNewline + } + + [System.Boolean] isfile([string]$path) { + return $path -and (Test-Path $path -PathType Leaf) + } + + [System.Boolean] isdir([string]$path) { + return $path -and (Test-Path $path -PathType Container) + } + + [void] mkdirs([string]$path) { + if (!(Test-Path $path -PathType Container)) { + New-Item $path -ItemType Directory 1>$null + } + } + + [void] rmdirs([string]$path) { + if ($this.isdir($path)) { + $this.println("Deleting $path ...") + Remove-Item $path -Recurse -Force + } + } + + [void] del([string]$path) { + if ($this.isfile($path)) { + $this.println("Deleting $path ...") + Remove-Item $path -Force + } + } + + [void] mv([string]$path, [string]$dest) { + if ($this.isdir($path) -and !$this.isdir($dest)) { + Move-Item $path $dest + } + } + [void] addpath([string]$path) { $this.addpath($path, $false) } + [void] addpath([string]$path, [bool]$append) { + if (!$path -or $env:PATH.Contains($path)) { return } + if (!$append) { $env:PATH = "$path$Global:ENV_PATH_SEP$env:PATH" } + else { $env:PATH = "$env:PATH$Global:ENV_PATH_SEP$path" } + } + + [void] pause($msg) { + $shoud_pause = $false + do { + if (!$Global:IsWin) { break } + $myProcess = [System.Diagnostics.Process]::GetCurrentProcess() + if ($myProcess.ProcessName.EndsWith('_ise')) { break } + $parentProcess = $myProcess.Parent + if (!$parentProcess) { + $myPID = $myProcess.Id + $instance = Get-WmiObject Win32_Process -Filter "ProcessId = $myPID" + $parentProcess = Get-Process -Id $instance.ParentProcessID -ErrorAction SilentlyContinue + if (!$parentProcess) { break } + } + + $executed_from_explorer = ($parentProcess.ProcessName -like "explorer") + if ($executed_from_explorer) { + $procesCmdLineArgs = "$([System.Environment]::GetCommandLineArgs())" + if ($procesCmdLineArgs.Contains('.ps1') -and !$procesCmdLineArgs.Contains('-noexit')) { + $shoud_pause = $true + } + } + } while ($false) + if ($shoud_pause) { + $this.print("$msg, press any key to continue . . .") + cmd /c pause 1>$null + } + else { + $this.println($msg) + } + } + + [bool] isabspath($path) { + return [IO.Path]::IsPathRooted($path) + } + + # Get full path without exist check + [string] realpath($path) { + return $Global:ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path) + } + + [string] hash($content) { + $stringAsStream = [System.IO.MemoryStream]::new() + $writer = [System.IO.StreamWriter]::new($stringAsStream) + $writer.write($content) + $writer.Flush() + $stringAsStream.Position = 0 + return (Get-FileHash -InputStream $stringAsStream -Algorithm MD5).Hash + } +} +$1k = [_1kiss]::new() + +# ---------------------- manifest -------------------- +# mode: +# x.y.z+ : >= +# x.y.z : == +# * : any +# x.y.z~x2.y2.z2 : range +$manifest = @{ + msvc = '14.39+'; # cl.exe @link.exe 14.39 VS2022 17.9.x + ndk = 'r23c'; + xcode = '13.0.0+'; # range + # _EMIT_STL_ERROR(STL1000, "Unexpected compiler version, expected Clang xx.x.x or newer."); + # clang-cl msvc14.37 require 16.0.0+ + # clang-cl msvc14.40 require 17.0.0+ + llvm = '17.0.6+'; + gcc = '9.0.0+'; + cmake = '3.23.0+'; + ninja = '1.10.0+'; + python = '3.8.0+'; + jdk = '17.0.10+'; # jdk17+ works for android cmdlinetools 7.0+ + emsdk = '3.1.53+'; + cmdlinetools = '7.0+'; # android cmdlinetools +} + +# the default generator requires explicit specified: osx, ios, android, wasm +$cmake_generators = @{ + 'android' = 'Ninja' + 'wasm' = 'Ninja' + 'wasm64' = 'Ninja' + 'osx' = 'Xcode' + 'ios' = 'Xcode' + 'tvos' = 'Xcode' + 'watchos' = 'Xcode' +} + +$channels = @{} + +# refer to: https://developer.android.com/studio#command-line-tools-only +$cmdlinetools_rev = '11076708' # 12.0 + +$android_sdk_tools = @{ + 'build-tools' = '34.0.0' + 'platforms' = 'android-34' +} + +# eva: evaluated_args +$options = @{ + p = $null + a = $null + d = $null + cc = $null + t = '' # cb_target + xt = 'cmake' + prefix = $null + xc = @() + xb = @() + j = -1 + O = -1 + sdk = '' + minsdk = $null + dll = $false + u = $false # whether delete 1kdist cross-platform prebuilt folder: path/to/_x + dm = $false # dump compiler preprocessors + i = $false # perform install + scope = 'local' +} + +$optName = $null +foreach ($arg in $args) { + if (!$optName) { + if ($arg.StartsWith('-')) { + $optName = $arg.SubString(1) + if ($optName.EndsWith(':')) { + $optName = $optName.TrimEnd(':') + } + $flag_tag = [string]$optName[0] + if ($flag_tag -in 'j', 'O') { + $flag_val = $null + if ([int]::TryParse($optName.substring(1), [ref] $flag_val)) { + $optName = $null + $options.$flag_tag = $flag_val + continue + } + } + if ($options[$optName] -is [bool]) { + $options[$optName] = $true + $optName = $null + } + } + } + else { + if ($options.Contains($optName)) { + $options[$optName] = $arg + } + else { + $1k.println("Warning: ignore unrecognized option: $optName") + } + $optName = $null + } +} + +# job count +if ($options.j -eq -1) { + $options.j = $([Environment]::ProcessorCount) +} + +# translate xtool args +if ($options.xc.GetType() -eq [string]) { + $options.xc = $options.xc.Split(' ') +} +if ($options.xb.GetType() -eq [string]) { + $options.xb = $options.xb.Split(' ') +} + +$pwsh_ver = $PSVersionTable.PSVersion.ToString() +if ([VersionEx]$pwsh_ver -lt [VersionEx]"7.0") { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +} + +$osVer = if ($IsWin) { "Microsoft Windows $([System.Environment]::OSVersion.Version.ToString())" } else { $PSVersionTable.OS } + +# arm64,x64 +# uname -m: arm64/aarch64,x86_64 +$HOST_CPU = [System.Runtime.InteropServices.RuntimeInformation, mscorlib]::OSArchitecture.ToString().ToLower() + +$1k.println("PowerShell $pwsh_ver on $osVer") + +# determine build target os +$TARGET_OS = $options.p +if (!$TARGET_OS) { + # choose host target if not specified by command line automatically + $TARGET_OS = $options.p = $('win32', 'linux', 'osx').Get($HOST_OS) +} +else { + $target_os_norm = @{winuwp = 'winrt'; mac = 'osx' }[$TARGET_OS] + if ($target_os_norm) { + $TARGET_OS = $target_os_norm + } +} + +$Global:target_minsdk = $options.minsdk +if (!$Global:target_minsdk) { + $Global:target_minsdk = @{osx = '10.15'; winrt = '10.0.17763.0' }[$TARGET_OS] +} + +# define some useful global vars +function eval($str, $raw = $false) { + if (!$raw) { + return Invoke-Expression "`"$str`"" + } + else { + return Invoke-Expression $str + } +} + +function create_symlink($sourcePath, $destPath) { + # try link ninja exist cmake bin directory + & "$myRoot\fsync.ps1" -s $sourcePath -d $destPath -l $true 2>$null + + if (!$? -and $IsWin) { + # try runas admin again + $mklink_args = "-Command ""& ""$myRoot\fsync.ps1"" -s '$sourcePath' -d '$destPath' -l `$true 2>`$null""" + Write-Host "mklink_args={$mklink_args}" + Start-Process powershell -ArgumentList $mklink_args -WindowStyle Hidden -Wait -Verb runas + } +} + +$Global:is_wasm = $TARGET_OS.StartsWith('wasm') +$Global:is_win32 = $TARGET_OS -eq 'win32' +$Global:is_winrt = $TARGET_OS -eq 'winrt' +$Global:is_mac = $TARGET_OS -eq 'osx' +$Global:is_linux = $TARGET_OS -eq 'linux' +$Global:is_android = $TARGET_OS -eq 'android' +$Global:is_ios = $TARGET_OS -eq 'ios' +$Global:is_tvos = $TARGET_OS -eq 'tvos' +$Global:is_watchos = $TARGET_OS -eq 'watchos' +$Global:is_ios_sim = $false +$Global:is_win_family = $Global:is_winrt -or $Global:is_win32 +$Global:is_darwin_embed_family = $Global:is_ios -or $Global:is_tvos -or $Global:is_watchos +$Global:is_darwin_family = $Global:is_mac -or $Global:is_darwin_embed_family +$Global:is_gh_act = "$env:GITHUB_ACTIONS" -eq 'true' + +$Script:cmake_ver = '' + +$Script:use_msvcr14x = $null +$null = [bool]::TryParse($env:use_msvcr14x, [ref]$Script:use_msvcr14x) + +if (!$is_wasm) { + $TARGET_CPU = $options.a + if (!$TARGET_CPU) { + $TARGET_CPU = @{'ios' = 'arm64'; 'tvos' = 'arm64'; 'watchos' = 'arm64'; 'android' = 'arm64'; }[$TARGET_OS] + if (!$TARGET_CPU) { + $TARGET_CPU = $HOST_CPU + } + $options.a = $TARGET_CPU + } + elseif ($TARGET_CPU -eq 'arm') { + $TARGET_CPU = $options.a = 'armv7' + } +} +else { + $TARGET_CPU = $options.a = '*' +} + +$Global:darwin_sim_suffix = $null +if ($Global:is_darwin_embed_family) { + if ($options.sdk.StartsWith('sim')) { + $Global:darwin_sim_suffix = '_sim' + } + $Global:is_ios_sim = $Global:darwin_sim_suffix -or ($TARGET_CPU -eq 'x64') +} +$Global:is_darwin_embed_device = $Global:is_darwin_embed_family -and !$Global:is_ios_sim + +if (!$setupOnly) { + $1k.println("$(Out-String -InputObject $options)") +} + +$HOST_OS_NAME = $('windows', 'linux', 'macos').Get($HOST_OS) + +# determine toolchain +$TOOLCHAIN = $options.cc +$toolchains = @{ + 'win32' = 'msvc'; + 'winrt' = 'msvc'; + 'linux' = 'gcc'; + 'android' = 'clang'; # xcode clang + 'osx' = 'clang'; # xcode clang + 'ios' = 'clang'; # xcode clang + 'tvos' = 'clang'; # xcode clang + 'watchos' = 'clang'; # xcode clang + 'wasm' = 'clang'; # emcc clang + 'wasm64' = 'clang'; # emcc clang +} +if (!$TOOLCHAIN) { + $TOOLCHAIN = $toolchains[$TARGET_OS] +} +if (!$TOOLCHAIN) { + throw "1kiss: Unsupported target os: $TARGET_OS" +} +$TOOLCHAIN_INFO = $TOOLCHAIN.Split('-') +$TOOLCHAIN_VER = $null +if ($TOOLCHAIN_INFO.Count -ge 2) { + $toolVer = $TOOLCHAIN_INFO[$TOOLCHAIN_INFO.Count - 1] + if ($toolVer -match "\d+") { + $TOOLCHAIN_NAME = $TOOLCHAIN_INFO[0..($TOOLCHAIN_INFO.Count - 2)] -join '-' + $TOOLCHAIN_VER = $toolVer + } +} +if (!$TOOLCHAIN_VER) { + $TOOLCHAIN_NAME = $TOOLCHAIN +} + +$Global:is_clang = $TOOLCHAIN_NAME -eq 'clang' +$Global:is_msvc = $TOOLCHAIN_NAME -eq 'msvc' + +$external_prefix = if ($options.prefix) { $options.prefix } else { Join-Path $HOME '.1kiss' } +if (!$1k.isdir($external_prefix)) { + $1k.mkdirs($external_prefix) +} + +$1k.println("proj_dir=$((Get-Location).Path), external_prefix=$external_prefix") + +# load toolset manifest +$manifest_file = Join-Path $myRoot 'manifest.ps1' +if ($1k.isfile($manifest_file)) { + . $manifest_file +} + +# 1kdist +$sentry_file = Join-Path $myRoot '.gitee' +$mirror = if ($1k.isfile($sentry_file)) { 'gitee' } else { 'github' } +$mirror_url_base = @{'github' = 'https://github.com/'; 'gitee' = 'https://gitee.com/' }[$mirror] +$1kdist_url_base = $mirror_url_base +$mirror_conf_file = $1k.realpath("$myRoot/../manifest.json") +$mirror_current = $null +$devtools_url_base = $null +$1kdist_ver = $null +if ($1k.isfile($mirror_conf_file)) { + $mirror_conf = ConvertFrom-Json (Get-Content $mirror_conf_file -raw) + $mirror_current = $mirror_conf.mirrors.$mirror + $1kdist_url_base += $mirror_current.'1kdist' + $devtools_url_base += "$1kdist_url_base/devtools" + $1kdist_ver = $mirror_conf.versions.'1kdist' + $1kdist_url_base += "/$1kdist_ver" +} + +function 1kdist_url($filename) { + return "$1kdist_url_base/$filename" +} + +function devtool_url($filename) { + return "$devtools_url_base/$filename" +} + +# accept x.y.z-rc1 +function version_eq($ver1, $ver2) { + return $ver1 -eq $ver2 +} +function version_like($ver1, $ver2) { + return $ver1 -like $ver2 +} + +# $ver2: accept x.y.z-rc1 +function version_ge($ver1, $ver2) { + $validatedVer = [Regex]::Match($ver1, '(\d+\.)+(-)?(\*|\d+)') + if ($validatedVer.Success) { + return [VersionEx]$validatedVer.Value -ge [VersionEx]$ver2 + } + return $false +} + +# $verMain and $verMax not accept x.y.z-rc1 +function version_in_range($ver1, $verMin, $verMax) { + $validatedVer = [Regex]::Match($ver1, '(\d+\.)+(-)?(\*|\d+)') + if ($validatedVer.Success) { + $typedVer1 = [VersionEx]$validatedVer.Value + $typedVerMin = [VersionEx]$verMin + $typedVerMax = [VersionEx]$verMax + return $typedVer1 -ge $typedVerMin -and $typedVer1 -le $typedVerMax + } + return $false +} + +# validate $env:PATH to avoid unexpected shell script behavior +if ([Regex]::Match($env:PATH, "`'|`"").Success) { + throw "Please remove any `' or `" from your PATH list" +} + +# validate cmd follow symlink recurse +function validate_cmd_fs($source, $root) { + $fileinfo = Get-Item $source + if (!$fileinfo.Target) { + if ($source -ne $root) { + $1k.println("info: the cmd follow symlink $root ==> $source") + } + return $true + } + $target = $fileinfo.Target + if (![IO.Path]::IsPathRooted($target)) { + # convert symlink target to fullpath + $target = $1k.realpath($(Join-Path $(Split-Path $source -Parent) $target)) + } + if (!$1k.isfile($target)) { + $1k.println("warning: the symlink target $root ==> $target is missing") + return $false + } + if ($target -eq $root) { + $1k.println("warning: detected cycle symlink for cmd $root") + return $true + } + return (validate_cmd_fs $target $root) +} + +function validate_cmd($source) { + return (validate_cmd_fs $source $source) +} + +function find_cmd($cmd) { + $cmd_info = (Get-Command $cmd -ErrorAction SilentlyContinue) + if ($cmd_info -and (validate_cmd $cmd_info.Source)) { + return $cmd_info + } + + return $null +} +function find_prog($name, $path = $null, $mode = 'ONLY', $cmd = $null, $params = @('--version'), $silent = $false, $usefv = $false) { + if ($path) { + $storedPATH = $env:PATH + if ($mode -eq 'ONLY') { + $env:PATH = $path + } + elseif ($mode -eq 'BOTH') { + $env:PATH = "$path$ENV_PATH_SEP$env:PATH" + } + } + else { + $storedPATH = $null + } + if (!$cmd) { $cmd = $name } + + $params = [array]$params + + # try get match expr and preferred ver + $checkVerCond = $null + $minimalVer = '' + $preferredVer = '' + $wildcardVer = '' + $requiredVer = $manifest[$name] + if ($requiredVer) { + $preferredVer = $null + if ($requiredVer -eq '*') { + $checkVerCond = '$True' + $preferredVer = 'latest' + } + else { + $verArr = $requiredVer.Split('~') + $isRange = $verArr.Count -gt 1 + $minimalVer = $verArr[0] + $preferredVer = $verArr[$isRange] + if ($preferredVer.EndsWith('+')) { + $preferredVer = $preferredVer.TrimEnd('+') + if ($minimalVer.EndsWith('+')) { $minimalVer = $minimalVer.TrimEnd('+') } + $checkVerCond = '$(version_ge $foundVer $minimalVer)' + } + else { + if ($isRange) { + $checkVerCond = '$(version_in_range $foundVer $minimalVer $preferredVer)' + } + else { + if (!$preferredVer.Contains('*')) { + $checkVerCond = '$(version_eq $foundVer $preferredVer)' + } else { + $wildcardVer = $preferredVer + $preferredVer = $wildcardVer.TrimEnd('.*') + $checkVerCond = '$(version_like $foundVer $wildcardVer)' + } + } + } + } + if (!$checkVerCond) { + throw "Invalid tool $name=$requiredVer in manifest" + } + } + + # find command + $cmd_info = find_cmd $cmd + + # needs restore immidiately since further cmd invoke maybe require system bins + if ($path) { + $env:PATH = "$path$ENV_PATH_SEP$storedPATH" + } + + $found_rets = $null # prog_path,prog_version + if ($cmd_info) { + $prog_path = $cmd_info.Source + + if (!$usefv) { + $verStr = $(. $cmd @params 2>$null) | Select-Object -First 1 + if (!$verStr -or $verStr.Contains('--version')) { + $verInfo = $cmd_info.Version + $verStr = "$($verInfo.Major).$($verInfo.Minor).$($verInfo.Build)" + } + + # can match x.y.z-rc3 or x.y.z-65a239b + $matchInfo = [Regex]::Match($verStr, '(\d+\.)+(\*|\d+)(\-[a-z0-9]+)?') + $foundVer = $matchInfo.Value + } + else { + $foundVer = "$($cmd_info.Version)" + } + [void]$minimalVer + if ($checkVerCond) { + $matched = Invoke-Expression $checkVerCond + if ($matched) { + if (!$silent) { $1k.println("Using ${name}: $prog_path, version: $foundVer") } + $found_rets = $prog_path, $foundVer + } + else { + # if (!$silent) { $1k.println("The installed ${name}: $prog_path, version: $foundVer not match required: $requiredVer") } + $found_rets = $null, $preferredVer + } + } + else { + if (!$silent) { $1k.println("Using ${name}: $prog_path, version: $foundVer") } + $found_rets = $prog_path, $foundVer + } + } + else { + if ($preferredVer) { + $found_rets = $null, $preferredVer + } + else { + throw "Not found $name, and it's not in manifest" + } + } + + if ($storedPATH) { + $env:PATH = $storedPATH + } + return $found_rets +} + +function exec_prog($prog, $params) { + # & $prog_name $params + for ($i = 0; $i -lt $params.Count; $i++) { + $param = "'" + $param += $params[$i] + $param += "'" + $params[$i] = $param + } + $strParams = "$params" + return Invoke-Expression -Command "$prog $strParams" +} + +function download_file($url, $out) { + if ($1k.isfile($out)) { return } + $1k.println("Downloading $url to $out ...") + Invoke-WebRequest -Uri $url -OutFile $out +} + +function download_and_expand($url, $out, $dest) { + + download_file $url $out + try { + $1k.mkdirs($dest) + if ($out.EndsWith('.zip')) { + Expand-Archive -Path $out -DestinationPath $dest + } + elseif ($out.EndsWith('.tar.gz')) { + tar xf "$out" -C $dest + } + elseif ($out.EndsWith('.7z') -or $out.EndsWith('.exe')) { + 7z x "$out" "-o$dest" -bsp1 -y | Out-Host + } + elseif ($out.EndsWith('.sh')) { + chmod 'u+x' "$out" + } + if (!$?) { throw "1kiss: Expand fail" } + } + catch { + Remove-Item $out -Force + throw "1kiss: Expand archive $out fail, please try again" + } +} + +function resolve_path ($path) { if ($1k.isabspath($path)) { $path } else { Join-Path $external_prefix $path } } +function fetch_pkg($url, $exrep = $null) { + $name = Split-Path $url -Leaf + $out = Join-Path $external_prefix $name + $dest = $external_prefix + + $pfn_rename = $null + + if ($exrep) { + $exrep = $exrep.Split('=') + if ($exrep.Count -eq 1) { + $dest = resolve_path $exrep[0] + $inst_loc = $dest + } + else { + # >=2 + $dest = $external_prefix + $inst_loc = resolve_path $exrep[1] + $pfn_rename = { + # move to plain folder name + $full_path = (Get-ChildItem -Path $external_prefix -Filter $exrep[0]).FullName + if ($full_path) { + $1k.mv($full_path, $inst_loc) + } + else { + throw "1kiss: rename $($exrep[0]) to $inst_loc fail" + } + } + } + } + else { + $dest = $external_prefix + $inst_loc = Join-Path $external_prefix $name + } + + if ($1k.isdir($inst_loc)) { $1k.rmdirs($inst_loc) } + download_and_expand $url $out $dest + + if ($pfn_rename) { &$pfn_rename } +} + +# setup nuget, not add to path +function setup_nuget() { + if (!$manifest['nuget']) { return $null } + $nuget_bin = Join-Path $external_prefix 'nuget' + $nuget_prog, $nuget_ver = find_prog -name 'nuget' -path $nuget_bin -mode 'BOTH' -silent $true + if (!$nuget_prog) { + $1k.rmdirs($nuget_bin) + $1k.mkdirs($nuget_bin) + + $nuget_prog = Join-Path $nuget_bin 'nuget.exe' + download_file "https://dist.nuget.org/win-x86-commandline/v$nuget_ver/nuget.exe" $nuget_prog + if (!$1k.isfile($nuget_prog)) { + throw "Install nuget fail" + } + } + $1k.addpath($nuget_bin) + $1k.println("Using nuget: $nuget_prog, version: $nuget_ver") + return $nuget_prog +} + +# setup python3, not install automatically +# ensure python3.exe is python.exe to solve unexpected error, i.e. +# google gclient require python3.exe, on windows 10/11 will locate to +# a dummy C:\Users\halx99\AppData\Local\Microsoft\WindowsApps\python3.exe cause +# shit strange error +function setup_python3() { + if (!$manifest['python']) { return $null } + $python_prog, $python_ver = find_prog -name 'python' + if (!$python_prog) { + throw "python $($manifest['python']) required!" + } + + $python3_prog, $_ = find_prog -name 'python' -cmd 'python3' -silent $true + if (!$python3_prog) { + $python3_prog = Join-Path (Split-Path $python_prog -Parent) (Split-Path $python_prog -Leaf).Replace('python', 'python3') + create_symlink $python_prog $python3_prog + } +} + +# setup glslcc, not add to path +function setup_glslcc() { + if (!$manifest['glslcc']) { return $null } + $glslcc_bin = Join-Path $external_prefix 'glslcc' + $glslcc_prog, $glslcc_ver = find_prog -name 'glslcc' -path $glslcc_bin -mode 'BOTH' + if ($glslcc_prog) { + return $glslcc_prog + } + + $suffix = $('win64.zip', 'linux.tar.gz', 'osx{0}.tar.gz').Get($HOST_OS) + if ($IsMacOS) { + if ([System.VersionEx]$glslcc_ver -ge [System.VersionEx]'1.9.4.1') { + $suffix = $suffix -f "-$HOST_CPU" + } + else { + $suffix = $suffix -f '' + } + } + + $glscc_base_url = $mirror_current.glslcc + fetch_pkg "$mirror_url_base$glscc_base_url/v$glslcc_ver/glslcc-$glslcc_ver-$suffix" $glslcc_bin + + $glslcc_prog = (Join-Path $glslcc_bin "glslcc$EXE_SUFFIX") + if ($1k.isfile($glslcc_prog)) { + $1k.println("Using glslcc: $glslcc_prog, version: $glslcc_ver") + return $glslcc_prog + } + + throw "Install glslcc fail" +} + +function setup_ninja() { + if (!$manifest['ninja']) { return $null } + $suffix = $('win', 'linux', 'mac').Get($HOST_OS) + $ninja_bin = Join-Path $external_prefix 'ninja' + $ninja_prog, $ninja_ver = find_prog -name 'ninja' + if ($ninja_prog) { + return $ninja_prog + } + + $ninja_prog, $ninja_ver = find_prog -name 'ninja' -path $ninja_bin -silent $true + if (!$ninja_prog) { + fetch_pkg "https://github.com/ninja-build/ninja/releases/download/v$ninja_ver/ninja-$suffix.zip" $ninja_bin + } + $1k.addpath($ninja_bin) + $ninja_prog = (Join-Path $ninja_bin "ninja$EXE_SUFFIX") + + $1k.println("Using ninja: $ninja_prog, version: $ninja_ver") + return $ninja_prog +} + +# setup cmake +function setup_cmake($skipOS = $false) { + $cmake_prog, $cmake_ver = find_prog -name 'cmake' + if ($cmake_prog -and (!$skipOS -or $cmake_prog.Contains($myRoot))) { + return $cmake_prog, $cmake_ver + } + + $cmake_root = $(Join-Path $external_prefix 'cmake') + $cmake_bin = Join-Path $cmake_root 'bin' + $cmake_prog, $cmake_ver = find_prog -name 'cmake' -path $cmake_bin -mode 'ONLY' -silent $true + if (!$cmake_prog) { + $1k.rmdirs($cmake_root) + + $cmake_suffix = @(".zip", ".sh", ".tar.gz").Get($HOST_OS) + if ($HOST_OS -ne $HOST_MAC) { + $cmake_pkg_name = "cmake-$cmake_ver-$HOST_OS_NAME-x86_64" + } + else { + $cmake_pkg_name = "cmake-$cmake_ver-$HOST_OS_NAME-universal" + } + + $cmake_pkg_path = Join-Path $external_prefix "$cmake_pkg_name$cmake_suffix" + + $assemble_url = $channels['cmake'] + if (!$assemble_url) { + $cmake_url = "https://github.com/Kitware/CMake/releases/download/v$cmake_ver/$cmake_pkg_name$cmake_suffix" + } + else { + $cmake_url = & $assemble_url -FileName "$cmake_pkg_name$cmake_suffix" + } + + $cmake_dir = Join-Path $external_prefix $cmake_pkg_name + if ($IsMacOS) { + $cmake_app_contents = Join-Path $cmake_dir 'CMake.app/Contents' + } + if (!$1k.isdir($cmake_dir)) { + fetch_pkg $cmake_url + } + + if ($1k.isdir($cmake_dir)) { + $cmake_root0 = $cmake_dir + if ($IsMacOS) { + $cmake_app_contents = Join-Path $cmake_dir 'CMake.app/Contents' + if ($1k.isdir($cmake_app_contents)) { + $cmake_root0 = $cmake_app_contents + } + sudo xattr -r -d com.apple.quarantine "$cmake_root0/bin/cmake" + } + $1k.mv($cmake_root0, $cmake_root) + + if ($1k.isdir($cmake_dir)) { + $1k.rmdirs($cmake_dir) + } + } + elseif ($IsLinux) { + if ($option.scope -ne 'global') { + $1k.mkdirs($cmake_root) + & "$cmake_pkg_path" '--skip-license' '--exclude-subdir' "--prefix=$cmake_root" 1>$null 2>$null + } + else { + $cmake_bin = '/usr/local/bin' + sudo bash "$cmake_pkg_path" '--skip-license' '--prefix=/usr/local' 1>$null 2>$null + } + if (!$?) { Remove-Item $cmake_pkg_path -Force } + } + + $cmake_prog, $_ = find_prog -name 'cmake' -path $cmake_bin -silent $true + if (!$cmake_prog) { + throw "Install cmake $cmake_ver fail" + } + + $1k.println("Using cmake: $cmake_prog, version: $cmake_ver") + } + + $1k.addpath($cmake_bin) + return $cmake_prog, $cmake_ver +} + +function ensure_cmake_ninja($cmake_prog, $ninja_prog) { + # ensure ninja in cmake_bin + $cmake_bin = Split-Path $cmake_prog -Parent + $cmake_ninja_prog, $__ = find_prog -name 'ninja' -path $cmake_bin -mode 'ONLY' -silent $true + if (!$cmake_ninja_prog) { + $ninja_symlink_target = Join-Path $cmake_bin (Split-Path $ninja_prog -Leaf) + # try link ninja exist cmake bin directory + create_symlink $ninja_prog $ninja_symlink_target + } + return $? +} + +function setup_nsis() { + if (!$manifest['nsis']) { return $null } + $nsis_bin = Join-Path $external_prefix "nsis" + $nsis_prog, $nsis_ver = find_prog -name 'nsis' -cmd 'makensis' -params '/VERSION' + if ($nsis_prog) { + return $nsis_prog + } + + $nsis_prog, $nsis_ver = find_prog -name 'nsis' -cmd 'makensis' -params '/VERSION' -path $nsis_bin -silent $true + if (!$nsis_prog) { + fetch_pkg "https://nchc.dl.sourceforge.net/project/nsis/NSIS%203/$nsis_ver/nsis-$nsis_ver.zip" "$nsis_bin-$nsis_ver=$nsis_bin" + } + $1k.addpath($nsis_bin) + $nsis_prog = (Join-Path $nsis_bin "makensis$EXE_SUFFIX") + $1k.println("Using nsis: $nsis_prog, version: $nsis_ver") + return $nsis_prog +} + +function setup_nasm() { + if (!$manifest['nasm']) { return $null } + $nasm_prog, $nasm_ver = find_prog -name 'nasm' -path "$external_prefix/nasm" -mode 'BOTH' -silent $true + + if (!$nasm_prog) { + if ($IsWin) { + $nasm_bin = Join-Path $external_prefix "nasm-$nasm_ver" + if (!$1k.isdir($nasm_bin)) { + fetch_pkg "https://www.nasm.us/pub/nasm/releasebuilds/$nasm_ver/win64/nasm-$nasm_ver-win64.zip" + } + $1k.addpath($nasm_bin) + } + elseif ($IsLinux) { + if ($(which dpkg)) { + sudo apt install nasm + } + } + elseif ($IsMacOS) { + brew install nasm + } + } + + $nasm_prog, $nasm_ver = find_prog -name 'nasm' -path "$external_prefix/nasm" -mode 'BOTH' -silent $true + if ($nasm_prog) { + $1k.println("Using nasm: $nasm_prog, version: $nasm_ver") + } +} + +function setup_jdk() { + if (!$manifest['jdk']) { return $null } + $arch_suffix = if ($HOST_CPU -eq 'x64') { 'x64' } else { 'aarch64' } + $suffix = $("windows-$arch_suffix.zip", "linux-$arch_suffix.tar.gz", "macOS-$arch_suffix.tar.gz").Get($HOST_OS) + $javac_prog, $jdk_ver = find_prog -name 'jdk' -cmd 'javac' + if ($javac_prog) { + return $javac_prog + } + + $jdk_root = Join-Path $external_prefix "jdk" + $java_home = if (!$IsMacOS) { $jdk_root } else { Join-Path $jdk_root 'Contents/Home' } + $jdk_bin = Join-Path $java_home 'bin' + + $javac_prog, $jdk_ver = find_prog -name 'jdk' -cmd 'javac' -path $jdk_bin -silent $true + if (!$javac_prog) { + fetch_pkg "https://aka.ms/download-jdk/microsoft-jdk-$jdk_ver-$suffix" "jdk-$jdk_ver+*=jdk" + } + + $env:JAVA_HOME = $java_home + $env:CLASSPATH = ".;$java_home\lib\dt.jar;$java_home\lib\tools.jar" + $1k.addpath($jdk_bin) + $javac_prog = find_prog -name 'jdk' -cmd 'javac' -path $jdk_bin -mode 'ONLY' -silent $true + if (!$javac_prog) { + throw "Install jdk $jdk_ver fail" + } + + $1k.println("Using jdk: $javac_prog, version: $jdk_ver") + + return $javac_prog +} + +function setup_7z() { + # ensure 7z_prog + $7z_cmd_info = Get-Command '7z' -ErrorAction SilentlyContinue + if (!$7z_cmd_info) { + if ($IsWin) { + $7z_prog = Join-Path $external_prefix '7z2301-x64/7z.exe' + if (!$1k.isfile($7z_prog)) { + fetch_pkg $(devtool_url '7z2301-x64.zip') + } + + $7z_bin = Split-Path $7z_prog -Parent + $1k.addpath($7z_bin) + } + elseif ($IsLinux) { + if ($(which dpkg)) { sudo apt install p7zip-full } + } + elseif ($IsMacOS) { + brew install p7zip + } + + $7z_cmd_info = Get-Command '7z' -ErrorAction SilentlyContinue + if (!$7z_cmd_info) { + throw "setup 7z fail" + } + } +} + +function setup_llvm() { + if (!$manifest.Contains('llvm')) { return $null } + $clang_prog, $clang_ver = find_prog -name 'llvm' -cmd "clang" + if (!$clang_prog) { + $llvm_root = Join-Path $external_prefix 'LLVM' + $llvm_bin = Join-Path $llvm_root 'bin' + $clang_prog, $clang_ver = find_prog -name 'llvm' -cmd "clang" -path $llvm_bin -silent $true + if (!$clang_prog) { + setup_7z + fetch_pkg "https://github.com/llvm/llvm-project/releases/download/llvmorg-${clang_ver}/LLVM-${clang_ver}-win64.exe" 'LLVM' + + $clang_prog, $clang_ver = find_prog -name 'llvm' -cmd "clang" -path $llvm_bin -silent $true + if (!$clang_prog) { + throw "setup $clang_ver fail" + } + } + $1k.addpath($llvm_bin) + $1k.println("Using llvm: $clang_prog, version: $clang_ver") + } +} + +function setup_android_sdk() { + if (!$manifest['ndk']) { return $null } + $ndk_ver = $TOOLCHAIN_VER + if (!$ndk_ver) { + $ndk_ver = $manifest['ndk'] + } + + $IsGraterThan = if ($ndk_ver.EndsWith('+')) { '+' } else { $null } + if ($IsGraterThan) { + $ndk_ver = $ndk_ver.Substring(0, $ndk_ver.Length - 1) + } + + $my_sdk_root = Join-Path $external_prefix 'adt/sdk' + + $sdk_dirs = @("$env:ANDROID_HOME", "$env:ANDROID_SDK_ROOT", $my_sdk_root) + + $ndk_minor_base = [int][char]'a' + + # looking up require ndk installed in exists sdk roots + $sdk_root = $null + foreach ($sdk_dir in $sdk_dirs) { + if (!$sdk_dir -or !$1k.isdir($sdk_dir)) { + continue + } + $1k.println("Looking require $ndk_ver$IsGraterThan in $sdk_dir") + $sdk_root = $sdk_dir + $ndk_root = $null + + $ndk_major = ($ndk_ver -replace '[^0-9]', '') + $ndk_minor_off = "$ndk_major".Length + 1 + $ndk_minor = if ($ndk_minor_off -lt $ndk_ver.Length) { "$([int][char]$ndk_ver.Substring($ndk_minor_off) - $ndk_minor_base)" } else { '0' } + $ndk_rev_base = "$ndk_major.$ndk_minor" + + $ndk_parent = Join-Path $sdk_dir 'ndk' + if (!$1k.isdir($ndk_parent)) { + continue + } + + # find ndk in sdk + $ndks = [ordered]@{} + $ndk_rev_max = '0.0' + foreach ($item in $(Get-ChildItem -Path "$ndk_parent")) { + $ndkDir = $item.FullName + $sourceProps = "$ndkDir/source.properties" + if ($1k.isfile($sourceProps)) { + $verLine = $(Get-Content $sourceProps | Select-Object -Index 1) + $ndk_rev = $($verLine -split '=').Trim()[1].split('.')[0..1] -join '.' + $ndks.Add($ndk_rev, $ndkDir) + if ($ndk_rev_max -le $ndk_rev) { + $ndk_rev_max = $ndk_rev + } + } + } + if ($IsGraterThan) { + if ($ndk_rev_max -ge $ndk_rev_base) { + $ndk_root = $ndks[$ndk_rev_max] + } + } + else { + $ndk_root = $ndks[$ndk_rev_base] + } + + if ($null -ne $ndk_root) { + $1k.println("Found $ndk_root in $sdk_root ...") + break + } + } + + if (!$1k.isdir("$ndk_root")) { + $sdkmanager_prog, $sdkmanager_ver = $null, $null + if ($1k.isdir($sdk_root)) { + $cmdlinetools_bin = Join-Path $sdk_root 'cmdline-tools/latest/bin' + $sdkmanager_prog, $sdkmanager_ver = (find_prog -name 'cmdlinetools' -cmd 'sdkmanager' -path $cmdlinetools_bin -params "--version", "--sdk_root=$sdk_root") + } + else { + $sdk_root = Join-Path $external_prefix 'adt/sdk' + if (!$1k.isdir($sdk_root)) { + $1k.mkdirs($sdk_root) + } + } + + if (!$sdkmanager_prog) { + $cmdlinetools_bin = Join-Path $external_prefix 'cmdline-tools/bin' + $sdkmanager_prog, $sdkmanager_ver = (find_prog -name 'cmdlinetools' -cmd 'sdkmanager' -path $cmdlinetools_bin -params "--version", "--sdk_root=$sdk_root") + $suffix = $('win', 'linux', 'mac').Get($HOST_OS) + if (!$sdkmanager_prog) { + $1k.println("Installing cmdlinetools version: $sdkmanager_ver ...") + + $cmdlinetools_pkg_name = "commandlinetools-$suffix-$($cmdlinetools_rev)_latest.zip" + $cmdlinetools_pkg_path = Join-Path $external_prefix $cmdlinetools_pkg_name + $cmdlinetools_url = "https://dl.google.com/android/repository/$cmdlinetools_pkg_name" + download_file $cmdlinetools_url $cmdlinetools_pkg_path + Expand-Archive -Path $cmdlinetools_pkg_path -DestinationPath "$external_prefix/" + $sdkmanager_prog, $_ = (find_prog -name 'cmdlinetools' -cmd 'sdkmanager' -path $cmdlinetools_bin -params "--version", "--sdk_root=$sdk_root" -silent $True) + if (!$sdkmanager_prog) { + throw "Install cmdlinetools version: $sdkmanager_ver fail" + } + } + } + + $matchInfos = (exec_prog -prog $sdkmanager_prog -params "--sdk_root=$sdk_root", '--list' | Select-String 'ndk;') + if ($null -ne $matchInfos -and $matchInfos.Count -gt 0) { + $1k.println("Not found suitable android ndk, installing ...") + + $ndks = @{} + foreach ($matchInfo in $matchInfos) { + $fullVer = $matchInfo.Line.Trim().Split(' ')[0] # "ndk;23.2.8568313" + $verNums = $fullVer.Split(';')[1].Split('.') + $ndkVer = 'r' + $ndkVer += $verNums[0] + + $ndk_minor = [int]$verNums[1] + if ($ndk_minor -gt 0) { + $ndkVer += [char]($ndk_minor_base + $ndk_minor) + } + if (!$ndks.Contains($ndkVer)) { + $ndks.Add($ndkVer, $fullVer) + } + } + + $ndkFullVer = $ndks[$ndk_ver] + + ((1..10 | ForEach-Object { "yes"; Start-Sleep -Milliseconds 100 }) | . $sdkmanager_prog --licenses --sdk_root=$sdk_root) | Out-Host + if (!$ndkOnly) { + exec_prog -prog $sdkmanager_prog -params '--verbose', "--sdk_root=$sdk_root", 'platform-tools', 'cmdline-tools;latest', "platforms;$($android_sdk_tools['platforms'])", "build-tools;$($android_sdk_tools['build-tools'])", $ndkFullVer | Out-Host + } + else { + exec_prog -prog $sdkmanager_prog -params '--verbose', "--sdk_root=$sdk_root", $ndkFullVer | Out-Host + } + $fullVer = $ndkFullVer.Split(';')[1] + $ndk_root = (Resolve-Path -Path "$sdk_root/ndk/$fullVer").Path + } + } + + return $sdk_root, $ndk_root +} + +# enable emsdk emcmake +function setup_emsdk() { + $emcc_prog, $emcc_ver = find_prog -name 'emsdk' -cmd 'emcc' -silent $true + if (!$emcc_prog) { + # no suitable emcc toolchain found, use official emsdk to setup + $1k.println('Not found emcc toolchain in $env:PATH, setup emsdk ...') + $emsdk_cmd = (Get-Command emsdk -ErrorAction SilentlyContinue) + if (!$emsdk_cmd) { + $emsdk_root = Join-Path $external_prefix 'emsdk' + if (!$1k.isdir($emsdk_root)) { + git clone 'https://github.com/emscripten-core/emsdk.git' $emsdk_root + } + else { + git -C $emsdk_root pull + } + } + else { + $emsdk_root = Split-Path $emsdk_cmd.Source -Parent + } + + $emcmake = (Get-Command emcmake -ErrorAction SilentlyContinue) + if (!$emcmake) { + Push-Location $emsdk_root + ./emsdk install $emcc_ver + ./emsdk activate $emcc_ver + . ./emsdk_env.ps1 + Pop-Location + } + } + else { + $1k.println("Using emcc: $emcc_prog, version: $emcc_ver") + } +} + +function setup_msvc() { + $cl_prog, $cl_ver = find_prog -name 'msvc' -cmd 'cl' -silent $true -usefv $true + if (!$cl_prog) { + if ($VS_INST) { + Import-Module "$VS_PATH\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" + $dev_cmd_args = "-arch=$target_cpu -host_arch=x64 -no_logo" + if (!$manifest['msvc'].EndsWith('+')) { $dev_cmd_args += " -vcvars_ver=$cl_ver" } + Enter-VsDevShell -VsInstanceId $VS_INST.instanceId -SkipAutomaticLocation -DevCmdArguments $dev_cmd_args + + $cl_prog, $cl_ver = find_prog -name 'msvc' -cmd 'cl' -silent $true -usefv $true + $1k.println("Using msvc: $cl_prog, version: $cl_ver") + } + else { + throw "Visual Studio not installed!" + } + } + + # msvc14x support + if ($Script:use_msvcr14x) { + if (!"$env:LIB".Contains('msvcr14x')) { + $msvcr14x_root = $env:msvcr14x_ROOT + $env:Platform = $target_cpu + Invoke-Expression -Command "$msvcr14x_root\msvcr14x_nmake.ps1" + } + + println "LIB=$env:LIB" + } +} + +function setup_xcode() { + $xcode_prog, $xcode_ver = find_prog -name 'xcode' -cmd "xcodebuild" -params @('-version') + if (!$xcode_prog) { + throw "Missing Xcode, please install" + } +} + +# google gn build system, current windows only for build angleproject/dawn on windows +function setup_gclient() { + $OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding + + if (!$ninja_prog) { + $ninja_prog = setup_ninja + } + + if ($Global:is_win_family) { + setup_msvc + } + + setup_python3 + + # setup gclient tool + # download depot_tools + # git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $gclient_dir + $gclient_dir = Join-Path $external_prefix 'depot_tools' + if (!$1k.isdir($gclient_dir)) { + if ($IsWin) { + $1k.mkdirs($gclient_dir) + Invoke-WebRequest -Uri "https://storage.googleapis.com/chrome-infra/depot_tools.zip" -OutFile "${gclient_dir}.zip" + Expand-Archive -Path "${gclient_dir}.zip" -DestinationPath $gclient_dir + } + else { + git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $gclient_dir + } + + } + $1k.addpath($gclient_dir, $true) + $env:DEPOT_TOOLS_WIN_TOOLCHAIN = 0 +} + +# +# Find latest installed: Visual Studio 12 2013 + +# installationVersion +# instanceId EnterDevShell can use it +# result: +# $Global:VS_VERSION +# $Global:VS_INST +# $Global:VS_PATH +# +$Global:VS_VERSION = $null +$Global:VS_PATH = $null +$Global:VS_INST = $null +function find_vs_latest() { + $vs_version = [VersionEx]'12.0' + if (!$Global:VS_INST) { + $VSWHERE_EXE = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $eap = $ErrorActionPreference + $ErrorActionPreference = 'SilentlyContinue' + + $vs_installs = ConvertFrom-Json "$(&$VSWHERE_EXE -version '12.0' -format 'json')" + $ErrorActionPreference = $eap + + if ($vs_installs) { + $vs_inst_latest = $null + foreach ($vs_inst in $vs_installs) { + $inst_ver = [VersionEx]$vs_inst.installationVersion + if ($vs_version -lt $inst_ver) { + $vs_version = $inst_ver + $vs_inst_latest = $vs_inst + } + } + $Global:VS_PATH = $vs_inst_latest.installationPath + $Global:VS_INST = $vs_inst_latest + } + } + $Global:VS_VERSION = $vs_version +} + +# preprocess methods: +# -inputOptions [CMAKE_OPTIONS] +function preprocess_win([string[]]$inputOptions) { + $outputOptions = $inputOptions + + if ($options.sdk) { + $outputOptions += "-DCMAKE_SYSTEM_VERSION=$($options.sdk)" + } + + if ($Global:is_msvc) { + # Generate vs2019 on github ci + # Determine arch name + $arch = if ($options.a -eq 'x86') { 'Win32' } else { $options.a } + + # arch + if ($VS_VERSION -ge [VersionEx]'16.0') { + $outputOptions += '-A', $arch + if ($TOOLCHAIN_VER) { + $outputOptions += "-Tv$TOOLCHAIN_VER" + } + } + else { + $gens = @{ + '120' = 'Visual Studio 12 2013'; + '140' = 'Visual Studio 14 2015' + "150" = 'Visual Studio 15 2017'; + } + $Script:cmake_generator = $gens[$TOOLCHAIN_VER] + if (!$Script:cmake_generator) { + throw "Unsupported toolchain: $TOOLCHAIN" + } + if ($options.a -eq "x64") { + $Script:cmake_generator += ' Win64' + } + } + + # platform + if ($Global:is_winrt) { + $outputOptions += '-DCMAKE_SYSTEM_NAME=WindowsStore', '-DCMAKE_SYSTEM_VERSION=10.0' + if ($Global:target_minsdk) { + $outputOptions += "-DCMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION=$Global:target_minsdk" + } + } + + if ($options.dll) { + $outputOptions += '-DBUILD_SHARED_LIBS=TRUE' + } + } + elseif ($Global:is_clang) { + $outputOptions += '-DCMAKE_C_COMPILER=clang', '-DCMAKE_CXX_COMPILER=clang++' + $Script:cmake_generator = 'Ninja Multi-Config' + } + else { + # Generate mingw + $Script:cmake_generator = 'Ninja Multi-Config' + } + return $outputOptions +} + +function preprocess_linux([string[]]$inputOptions) { + $outputOptions = $inputOptions + if ($Global:is_clang) { + $outputOptions += '-DCMAKE_C_COMPILER=clang', '-DCMAKE_CXX_COMPILER=clang++' + } + return $outputOptions +} + +$ninja_prog = $null +$is_gradlew = $options.xt.Contains('gradlew') +function preprocess_andorid([string[]]$inputOptions) { + $outputOptions = $inputOptions + + $t_archs = @{arm64 = 'arm64-v8a'; armv7 = 'armeabi-v7a'; x64 = 'x86_64'; x86 = 'x86'; } + + if ($is_gradlew) { + if ($options.a.GetType() -eq [object[]]) { + $archlist = [string[]]$options.a + } + else { + $archlist = $options.a.Split(';') + } + for ($i = 0; $i -lt $archlist.Count; ++$i) { + $arch = $archlist[$i] + $archlist[$i] = $t_archs[$arch] + } + + $archs = $archlist -join ':' # TODO: modify gradle, split by ';' + + $outputOptions += "-P__1K_CMAKE_VERSION=$($Script:cmake_ver.TrimLast('-'))" + $outputOptions += "-P__1K_ARCHS=$archs" + $outputOptions += '--parallel', '--info' + } + else { + if (!$ndk_root) { + throw "ndk_root not specified!" + } + $cmake_toolchain_file = Join-Path $ndk_root 'build/cmake/android.toolchain.cmake' + $arch = $t_archs[$options.a] + $outputOptions += "-DCMAKE_TOOLCHAIN_FILE=$cmake_toolchain_file", "-DANDROID_ABI=$arch" + # If set to ONLY, then only the roots in CMAKE_FIND_ROOT_PATH will be searched + # If set to BOTH, then the host system paths and the paths in CMAKE_FIND_ROOT_PATH will be searched + # If set to NEVER, then the roots in CMAKE_FIND_ROOT_PATH will be ignored and only the host system root will be used + # CMAKE_FIND_ROOT_PATH is preferred for additional search directories when cross-compiling + # $outputOptions += '-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH' + # $outputOptions += '-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH' + # $outputOptions += '-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH' + # by default, we want find host program only when cross-compiling + $outputOptions += '-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER' + } + + return $outputOptions +} + +function preprocess_osx([string[]]$inputOptions) { + $outputOptions = $inputOptions + $arch = $options.a + if ($arch -eq 'x64') { + $arch = 'x86_64' + } + + $outputOptions += "-DCMAKE_OSX_ARCHITECTURES=$arch" + if ($Global:target_minsdk) { + $outputOptions += "-DCMAKE_OSX_DEPLOYMENT_TARGET=$Global:target_minsdk" + } + return $outputOptions +} + +# build ios famliy (ios,tvos,watchos) +function preprocess_ios([string[]]$inputOptions) { + $outputOptions = $inputOptions + $arch = $options.a + if ($arch -eq 'x64') { + $arch = 'x86_64' + } + if (!$cmake_toolchain_file) { + $cmake_toolchain_file = Join-Path $myRoot 'ios.cmake' + $outputOptions += "-DCMAKE_TOOLCHAIN_FILE=$cmake_toolchain_file", "-DARCHS=$arch" + if ($Global:is_tvos) { + $outputOptions += '-DPLAT=tvOS' + } + elseif ($Global:is_watchos) { + $outputOptions += '-DPLAT=watchOS' + } + if ($Global:is_ios_sim) { + $outputOptions += '-DSIMULATOR=TRUE' + } + } + return $outputOptions +} + +function preprocess_wasm([string[]]$inputOptions) { + if ($options.p -eq 'wasm64') { $inputOptions += '-DCMAKE_C_FLAGS="-Wno-experimental -sMEMORY64"', '-DCMAKE_CXX_FLAGS="-Wno-experimental -sMEMORY64"', '-DEMSCRIPTEN_SYSTEM_PROCESSOR=x86_64' } + return $inputOptions +} + +function validHostAndToolchain() { + $appleTable = @{ + 'host' = @{'macos' = $True }; + 'toolchain' = @{'clang' = $True; }; + }; + $validTable = @{ + 'win32' = @{ + 'host' = @{'windows' = $True }; + 'toolchain' = @{'msvc' = $True; 'clang' = $True; 'gcc' = $True }; + }; + 'winrt' = @{ + 'host' = @{'windows' = $True }; + 'toolchain' = @{'msvc' = $True; }; + }; + 'linux' = @{ + 'host' = @{'linux' = $True }; + 'toolchain' = @{'gcc' = $True; 'clang' = $True }; + }; + 'android' = @{ + 'host' = @{'windows' = $True; 'linux' = $True; 'macos' = $True }; + 'toolchain' = @{'clang' = $True; }; + }; + 'osx' = $appleTable; + 'ios' = $appleTable; + 'tvos' = $appleTable; + 'watchos' = $appleTable; + 'wasm' = @{ + 'host' = @{'windows' = $True; 'linux' = $True; 'macos' = $True }; + 'toolchain' = @{'clang' = $True; }; + }; + 'wasm64' = @{ + 'host' = @{'windows' = $True; 'linux' = $True; 'macos' = $True }; + 'toolchain' = @{'clang' = $True; }; + }; + } + $validInfo = $validTable[$TARGET_OS] + $validOS = $validInfo.host[$HOST_OS_NAME] + if (!$validOS) { + throw "Can't build target $TARGET_OS on $HOST_OS_NAME" + } + $validToolchain = $validInfo.toolchain[$TOOLCHAIN_NAME] + if (!$validToolchain) { + throw "Can't build target $TARGET_OS with toolchain $TOOLCHAIN_NAME" + } +} + +$proprocessTable = @{ + 'win32' = ${function:preprocess_win}; + 'winrt' = ${function:preprocess_win}; + 'linux' = ${function:preprocess_linux}; + 'android' = ${function:preprocess_andorid}; + 'osx' = ${function:preprocess_osx}; + 'ios' = ${function:preprocess_ios}; + 'tvos' = ${function:preprocess_ios}; + 'watchos' = ${function:preprocess_ios}; + 'wasm' = ${Function:preprocess_wasm}; + 'wasm64' = ${Function:preprocess_wasm}; +} + +validHostAndToolchain + +########## setup build tools if not installed ####### + +$null = setup_glslcc + +$cmake_prog, $Script:cmake_ver = setup_cmake + +if ($Global:is_win_family) { + find_vs_latest + $nuget_prog = setup_nuget +} + +if ($Global:is_win32) { + $nsis_prog = setup_nsis + if (!$Global:is_msvc) { + $ninja_prog = setup_ninja + } + if ($Global:is_clang) { + $null = setup_llvm + } +} +elseif ($Global:is_android) { + $ninja_prog = setup_ninja + # ensure ninja in cmake_bin + if (!(ensure_cmake_ninja $cmake_prog $ninja_prog)) { + $cmake_prog, $Script:cmake_ver = setup_cmake -skipOS $true + if (!(ensure_cmake_ninja $cmake_prog $ninja_prog)) { + $1k.println("Ensure ninja in cmake bin directory fail") + } + } + + $null = setup_jdk # setup android sdk cmdlinetools require jdk + $sdk_root, $ndk_root = setup_android_sdk + $env:ANDROID_HOME = $sdk_root + $env:ANDROID_NDK = $ndk_root + # sync ndk env vars for some library required, i.e. will fix openssl issues: + # no NDK xxx-linux-android-gcc on $PATH at (eval 10) line 142. + # Note: github action vm also have follow env vars + $env:ANDROID_NDK_HOME = $ndk_root + $env:ANDROID_NDK_ROOT = $ndk_root + + $ndk_host = @('windows', 'linux', 'darwin').Get($HOST_OS) + $env:ANDROID_NDK_BIN = Join-Path $ndk_root "toolchains/llvm/prebuilt/$ndk_host-x86_64/bin" + function active_ndk_toolchain() { + $1k.addpath($env:ANDROID_NDK_BIN) + $clang_prog, $clang_ver = find_prog -name 'clang' + } +} +elseif ($Global:is_wasm) { + $ninja_prog = setup_ninja + . setup_emsdk +} + +$is_host_target = $Global:is_win32 -or $Global:is_linux -or $Global:is_mac +$is_host_cpu = $HOST_CPU -eq $TARGET_CPU + +if (!$setupOnly) { + $BUILD_DIR = $null + $SOURCE_DIR = $null + + function resolve_out_dir($prefix) { + if ($is_host_target) { + if (!$is_host_cpu) { + $out_dir = "${prefix}${TARGET_CPU}" + } else { + $out_dir = $prefix.TrimEnd("_") + } + } + else { + $out_dir = "${prefix}${TARGET_OS}" + if ($TARGET_CPU -ne '*') { + $out_dir += "_$TARGET_CPU" + } + } + if ($Global:is_ios_sim) { + $out_dir += $Global:darwin_sim_suffix + } + return $1k.realpath($out_dir) + } + + $stored_cwd = $(Get-Location).Path + if ($options.d) { + Set-Location $options.d + } + + # parsing build optimize flag from build_options + $buildOptions = [array]$options.xb + $nopts = $buildOptions.Count + $optimize_flag = $null + if ($options.O -ne -1) { + $optimize_flag = @('Debug', 'MinSizeRel', 'RelWithDebInfo', 'Release')[$options.O] + } + $evaluated_build_args = @() + for ($i = 0; $i -lt $nopts; ++$i) { + $optv = $buildOptions[$i] + switch ($optv) { + '--config' { + $optimize_flag = $buildOptions[$i++ + 1] + } + '--target' { + $cmake_target = $buildOptions[$i++ + 1] + } + default { + $evaluated_build_args += $optv + } + } + } + + if ($options.xt -ne 'gn') { + $BUILD_ALL_OPTIONS = $evaluated_build_args + if (!$optimize_flag) { + $optimize_flag = 'Release' + } + $BUILD_ALL_OPTIONS += '--config', $optimize_flag + + # enter building steps + $1k.println("Building target $TARGET_OS on $HOST_OS_NAME with toolchain $TOOLCHAIN ...") + + # step1. preprocess cross make options + $CONFIG_ALL_OPTIONS = [array]$(& $proprocessTable[$TARGET_OS] -inputOptions @() ) + + if (!$CONFIG_ALL_OPTIONS) { + $CONFIG_ALL_OPTIONS = @() + } + + if ($options.u) { + $CONFIG_ALL_OPTIONS += '-D_1KFETCH_UPGRADE=TRUE' + } + else { + $CONFIG_ALL_OPTIONS += '-D_1KFETCH_UPGRADE=FALSE' + } + + # determine generator, build_dir, inst_dir for non gradlew projects + if (!$is_gradlew) { + $INST_DIR = $null + $xopt_presets = 0 + $xprefix_optname = '-DCMAKE_INSTALL_PREFIX=' + $xopts = [array]$options.xc + $evaluated_xopts = @() + for ($opti = 0; $opti -lt $xopts.Count; ++$opti) { + $opt = $xopts[$opti] + if ($opt.StartsWith('-B')) { + if ($opt.Length -gt 2) { + $BUILD_DIR = $opt.Substring(2).Trim() + } + elseif (++$opti -lt $xopts.Count) { + $BUILD_DIR = $xopts[$opti] + } + ++$xopt_presets + } + elseif ($opt.StartsWith('-S')) { + if ($opt.Length -gt 2) { + $SOURCE_DIR = $opt.Substring(2).Trim() + } + elseif (++$opti -lt $xopts.Count) { + $SOURCE_DIR = $xopts[$opti] + } + ++$xopt_presets + } + elseif ($opt.startsWith('-G')) { + if ($opt.Length -gt 2) { + $cmake_generator = $opt.Substring(2).Trim() + } + elseif (++$opti -lt $xopts.Count) { + $cmake_generator = $xopts[$opti] + } + ++$xopt_presets + } + elseif ($opt.StartsWith($xprefix_optname)) { + ++$xopt_presets + $INST_DIR = $opt.SubString($xprefix_optname.Length) + } + else { + $evaluated_xopts += $opt + } + } + + if (!$cmake_generator -and !$TARGET_OS.StartsWith('win') -and $TARGET_OS -ne 'linux') { + $cmake_generator = $cmake_generators[$TARGET_OS] + if ($null -eq $cmake_generator) { + $cmake_generator = if (!$IsWin) { 'Unix Makefiles' } else { 'Ninja' } + } + } + + if ($cmake_generator) { + $using_ninja = $cmake_generator.StartsWith('Ninja') + + if (!$is_wasm) { + $CONFIG_ALL_OPTIONS += '-G', $cmake_generator + } + + if ($cmake_generator -eq 'Unix Makefiles' -or $using_ninja) { + $CONFIG_ALL_OPTIONS += "-DCMAKE_BUILD_TYPE=$optimize_flag" + } + + if ($using_ninja -and $Global:is_android) { + $CONFIG_ALL_OPTIONS += "-DCMAKE_MAKE_PROGRAM=$ninja_prog" + } + + if ($cmake_generator -eq 'Xcode') { + setup_xcode + } + } + + if (!$BUILD_DIR) { + $BUILD_DIR = resolve_out_dir 'build_' + } + if (!$INST_DIR) { + $INST_DIR = resolve_out_dir 'install_' + } + + if ($rebuild) { + $1k.rmdirs($BUILD_DIR) + $1k.rmdirs($INST_DIR) + } + } + else { + # android gradle + # replace all cmake config options -DXXX to -P_1K_XXX + $evaluated_xopts = @() + foreach ($opt in $options.xc) { + if ($opt.startsWith('-D')) { + $evaluated_xopts += "-P_1K_$($opt.substring(2))" + } + elseif ($opt.startsWith('-P')) { + $evaluated_xopts += $opt + } # ignore unknown option type + } + } + + # step2. apply additional cross make options + if ($evaluated_xopts.Count -gt 0) { + $1k.println("Apply additional cross make options: $($evaluated_xopts), Count={0}" -f $evaluated_xopts.Count) + $CONFIG_ALL_OPTIONS += $evaluated_xopts + } + + $1k.println("CONFIG_ALL_OPTIONS=$CONFIG_ALL_OPTIONS, Count={0}" -f $CONFIG_ALL_OPTIONS.Count) + + if ($Global:is_android -and $is_gradlew) { + $build_tool = (Get-Command $options.xt).Source + $build_tool_dir = Split-Path $build_tool -Parent + Push-Location $build_tool_dir + if (!$configOnly) { + if ($optimize_flag -eq 'Debug') { + & $build_tool assembleDebug $CONFIG_ALL_OPTIONS | Out-Host + } + else { + & $build_tool assembleRelease $CONFIG_ALL_OPTIONS | Out-Host + } + } + else { + if ($optimize_flag -eq 'Debug') { + & $build_tool configureCMakeDebug prepareKotlinBuildScriptModel $CONFIG_ALL_OPTIONS | Out-Host + } + else { + & $build_tool configureCMakeRelWithDebInfo prepareKotlinBuildScriptModel $CONFIG_ALL_OPTIONS | Out-Host + } + } + Pop-Location + } + else { + # step3. configure + $workDir = $(Get-Location).Path + $cmakeEntryFile = 'CMakeLists.txt' + $mainDep = if (!$SOURCE_DIR) { Join-Path $workDir $cmakeEntryFile } else { $(Join-Path $SOURCE_DIR $cmakeEntryFile) } + if ($1k.isfile($mainDep)) { + $mainDepChanged = $false + # A Windows file time is a 64-bit value that represents the number of 100-nanosecond + $tempFileItem = Get-Item $mainDep + $lastWriteTime = $tempFileItem.LastWriteTime.ToFileTimeUTC() + $tempFile = Join-Path $BUILD_DIR '1k_cache.txt' + + $storeHash = 0 + if ($1k.isfile($tempFile)) { + $storeHash = Get-Content $tempFile -Raw + } + $hashValue = $1k.hash("$CONFIG_ALL_OPTIONS#$lastWriteTime") + $mainDepChanged = "$storeHash" -ne "$hashValue" + $cmakeCachePath = $1k.realpath("$BUILD_DIR/CMakeCache.txt") + + if ($mainDepChanged -or !$1k.isfile($cmakeCachePath) -or $forceConfig) { + $config_cmd = if (!$is_wasm) { 'cmake' } else { 'emcmake' } + if ($is_wasm) { + $CONFIG_ALL_OPTIONS = @('cmake') + $CONFIG_ALL_OPTIONS + } + + if ($options.dm) { + $1k.println("Dumping compiler preprocessors ...") + $dm_dir = Join-Path $PSScriptRoot 'dm' + $dm_build_dir = Join-Path $dm_dir 'build' + &$config_cmd $CONFIG_ALL_OPTIONS -S $dm_dir -B $dm_build_dir | Out-Host ; Remove-Item $dm_build_dir -Recurse -Force + $1k.println("Finish dump compiler preprocessors") + } + $CONFIG_ALL_OPTIONS += '-B', $BUILD_DIR, "-DCMAKE_INSTALL_PREFIX=$INST_DIR" + if ($SOURCE_DIR) { $CONFIG_ALL_OPTIONS += '-S', $SOURCE_DIR } + $1k.println("CMake config command: $config_cmd $CONFIG_ALL_OPTIONS") + &$config_cmd $CONFIG_ALL_OPTIONS | Out-Host + Set-Content $tempFile $hashValue -NoNewline + } + + if (!$configOnly) { + if (!$is_gradlew) { + if (!$1k.isfile($cmakeCachePath)) { + Set-Location $stored_cwd + throw "The cmake generate incomplete, pelase add '-f' to re-generate again" + } + } + + # step4. build + # apply additional build options + $BUILD_ALL_OPTIONS += "--parallel", "$($options.j)" + + if ($options.t) { $cmake_target = $options.t } + if ($cmake_target) { $BUILD_ALL_OPTIONS += '--target', $cmake_target } + $1k.println("BUILD_ALL_OPTIONS=$BUILD_ALL_OPTIONS, Count={0}" -f $BUILD_ALL_OPTIONS.Count) + + # forward non-cmake args to underlaying build toolchain, must at last + if (($cmake_generator -eq 'Xcode') -and !$BUILD_ALL_OPTIONS.Contains('--verbose')) { + $BUILD_ALL_OPTIONS += '--', '-quiet' + } + $1k.println("cmake --build $BUILD_DIR $BUILD_ALL_OPTIONS") + cmake --build $BUILD_DIR $BUILD_ALL_OPTIONS | Out-Host + if (!$?) { + Set-Location $stored_cwd + exit $LASTEXITCODE + } + + if ($options.i) { + $install_args = @($BUILD_DIR, '--config', $optimize_flag) + cmake --install $install_args | Out-Host + } + } + } + else { + $1k.println("Missing file: $cmakeEntryFile") + } + } + + Set-Location $stored_cwd + } + else { + # google gclient/gn build system + # refer: https://chromium.googlesource.com/chromium/src/+/eca97f87e275a7c9c5b7f13a65ff8635f0821d46/tools/gn/docs/reference.md#args_specifies-build-arguments-overrides-examples + + $stored_env_path = $null + $gn_buildargs_overrides = @() + + if ($Global:is_winrt) { + $gn_buildargs_overrides += 'target_os=\"winuwp\"' + } + elseif ($Global:is_ios) { + $gn_buildargs_overrides += 'target_os=\"ios\"' + if ($Global:is_ios_sim) { + $gn_buildargs_overrides += 'target_environment=\"simulator\"' + } + } + elseif ($Global:is_android) { + $gn_buildargs_overrides += 'target_os=\"android\"' + $stored_env_path = $env:PATH + active_ndk_toolchain + } + $gn_target_cpu = if ($TARGET_CPU -ne 'armv7') { $TARGET_CPU } else { 'arm' } + $gn_buildargs_overrides += "target_cpu=\`"$gn_target_cpu\`"" + + if ($options.xc) { + $gn_buildargs_overrides += $options.xc + } + + if ($Global:is_darwin_embed_device) { + $gn_buildargs_overrides += 'ios_enable_code_signing=false' + } + + if ($Script:use_msvcr14x) { + $gn_buildargs_overrides += 'use_msvcr14x=true' + } + + if ($optimize_flag -eq 'Debug') { + $gn_buildargs_overrides += 'is_debug=true' + } + else { + $gn_buildargs_overrides += 'is_debug=false' + } + + Write-Output ("gn_buildargs_overrides=$gn_buildargs_overrides, Count={0}" -f $gn_buildargs_overrides.Count) + + $BUILD_DIR = resolve_out_dir 'out/' + + if ($rebuild) { + $1k.rmdirs($BUILD_DIR) + } + + $gn_gen_args = @('gen', $BUILD_DIR) + if ($Global:is_win_family) { + $sln_name = Split-Path $(Get-Location).Path -Leaf + $gn_gen_args += '--ide=vs2022', "--sln=$sln_name" + } + + if ($gn_buildargs_overrides) { + $gn_gen_args += "--args=`"$gn_buildargs_overrides`"" + } + + Write-Output "Executing command: {gn $gn_gen_args}" + + # Note: + # 1. powershell 7.2.12 works: gn $gn_gen_args, but 7.4.0 not works + # 2. only --args="target_cpu=\"x64\"" works + # 3. --args='target_cpu="x64" not work invoke from pwsh + if ($IsWin) { + cmd /c "gn $gn_gen_args" + } + else { + if ([VersionEx]$pwsh_ver -ge [VersionEx]'7.3') { + bash -c "gn $gn_gen_args" + } + else { + gn $gn_gen_args + } + } + + # build + autoninja -C $BUILD_DIR --verbose $options.t + + # restore env:PATH + if ($stored_env_path) { + $env:PATH = $stored_env_path + } + } + + $Global:BUILD_DIR = $BUILD_DIR + $env:buildResult = ConvertTo-Json @{ + buildDir = $BUILD_DIR + targetOS = $TARGET_OS + targetCPU = $TARGET_CPU + hostCPU = $HOST_CPU + isHostTarget = $is_host_target + compilerID = $TOOLCHAIN_NAME + } +} + +exit $LASTEXITCODE diff --git a/NativeLibs/yasio/1k/CPM.cmake b/NativeLibs/yasio/1k/CPM.cmake new file mode 100644 index 0000000..2e37676 --- /dev/null +++ b/NativeLibs/yasio/1k/CPM.cmake @@ -0,0 +1,1154 @@ +# CPM.cmake - CMake's missing package manager +# =========================================== +# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions. +# +# MIT License +# ----------- +#[[ + Copyright (c) 2019-2022 Lars Melchior and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +]] + +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +# Initialize logging prefix +if(NOT CPM_INDENT) + set(CPM_INDENT + "CPM:" + CACHE INTERNAL "" + ) +endif() + +if(NOT COMMAND cpm_message) + function(cpm_message) + message(${ARGV}) + endfunction() +endif() + +set(CURRENT_CPM_VERSION 0.38.2) + +get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) +if(CPM_DIRECTORY) + if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY) + if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION) + message( + AUTHOR_WARNING + "${CPM_INDENT} \ +A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ +It is recommended to upgrade CPM to the most recent version. \ +See https://github.com/cpm-cmake/CPM.cmake for more information." + ) + endif() + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + include(FetchContent) + endif() + return() + endif() + + get_property( + CPM_INITIALIZED GLOBAL "" + PROPERTY CPM_INITIALIZED + SET + ) + if(CPM_INITIALIZED) + return() + endif() +endif() + +if(CURRENT_CPM_VERSION MATCHES "development-version") + message( + WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \ +Please update to a recent release if possible. \ +See https://github.com/cpm-cmake/CPM.cmake for details." + ) +endif() + +set_property(GLOBAL PROPERTY CPM_INITIALIZED true) + +macro(cpm_set_policies) + # the policy allows us to change options without caching + cmake_policy(SET CMP0077 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + + # the policy allows us to change set(CACHE) without caching + if(POLICY CMP0126) + cmake_policy(SET CMP0126 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) + endif() + + # The policy uses the download time for timestamp, instead of the timestamp in the archive. This + # allows for proper rebuilds when a projects url changes + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) + endif() +endmacro() +cpm_set_policies() + +option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" + $ENV{CPM_USE_LOCAL_PACKAGES} +) +option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" + $ENV{CPM_LOCAL_PACKAGES_ONLY} +) +option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) +option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" + $ENV{CPM_DONT_UPDATE_MODULE_PATH} +) +option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" + $ENV{CPM_DONT_CREATE_PACKAGE_LOCK} +) +option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + "Add all packages added through CPM.cmake to the package lock" + $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK} +) +option(CPM_USE_NAMED_CACHE_DIRECTORIES + "Use additional directory of package name in cache on the most nested level." + $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES} +) + +set(CPM_VERSION + ${CURRENT_CPM_VERSION} + CACHE INTERNAL "" +) +set(CPM_DIRECTORY + ${CPM_CURRENT_DIRECTORY} + CACHE INTERNAL "" +) +set(CPM_FILE + ${CMAKE_CURRENT_LIST_FILE} + CACHE INTERNAL "" +) +set(CPM_PACKAGES + "" + CACHE INTERNAL "" +) +set(CPM_DRY_RUN + OFF + CACHE INTERNAL "Don't download or configure dependencies (for testing)" +) + +if(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) +else() + set(CPM_SOURCE_CACHE_DEFAULT OFF) +endif() + +set(CPM_SOURCE_CACHE + ${CPM_SOURCE_CACHE_DEFAULT} + CACHE PATH "Directory to download CPM dependencies" +) + +if(NOT CPM_DONT_UPDATE_MODULE_PATH) + set(CPM_MODULE_PATH + "${CMAKE_BINARY_DIR}/CPM_modules" + CACHE INTERNAL "" + ) + # remove old modules + file(REMOVE_RECURSE ${CPM_MODULE_PATH}) + file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) + # locally added CPM modules should override global packages + set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") +endif() + +if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + set(CPM_PACKAGE_LOCK_FILE + "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" + CACHE INTERNAL "" + ) + file(WRITE ${CPM_PACKAGE_LOCK_FILE} + "# CPM Package Lock\n# This file should be committed to version control\n\n" + ) +endif() + +include(FetchContent) + +# Try to infer package name from git repository uri (path or url) +function(cpm_package_name_from_git_uri URI RESULT) + if("${URI}" MATCHES "([^/:]+)/?.git/?$") + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + else() + unset(${RESULT} PARENT_SCOPE) + endif() +endfunction() + +# Try to infer package name and version from a url +function(cpm_package_name_and_ver_from_url url outName outVer) + if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)") + # We matched an archive + set(filename "${CMAKE_MATCH_1}") + + if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)") + # We matched - (ie foo-1.2.3) + set(${outName} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + set(${outVer} + "${CMAKE_MATCH_2}" + PARENT_SCOPE + ) + elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)") + # We couldn't find a name, but we found a version + # + # In many cases (which we don't handle here) the url would look something like + # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly + # distinguish the package name from the irrelevant bits. Moreover if we try to match the + # package name from the filename, we'd get bogus at best. + unset(${outName} PARENT_SCOPE) + set(${outVer} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + else() + # Boldly assume that the file name is the package name. + # + # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but + # such cases should be quite rare. No popular service does this... we think. + set(${outName} + "${filename}" + PARENT_SCOPE + ) + unset(${outVer} PARENT_SCOPE) + endif() + else() + # No ideas yet what to do with non-archives + unset(${outName} PARENT_SCOPE) + unset(${outVer} PARENT_SCOPE) + endif() +endfunction() + +function(cpm_find_package NAME VERSION) + string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") + find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) + if(${CPM_ARGS_NAME}_FOUND) + if(DEFINED ${CPM_ARGS_NAME}_VERSION) + set(VERSION ${${CPM_ARGS_NAME}_VERSION}) + endif() + cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}") + CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}") + set(CPM_PACKAGE_FOUND + YES + PARENT_SCOPE + ) + else() + set(CPM_PACKAGE_FOUND + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from +# finding the system library +function(cpm_create_module_file Name) + if(NOT CPM_DONT_UPDATE_MODULE_PATH) + # erase any previous modules + file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake + "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)" + ) + endif() +endfunction() + +# Find a package locally or fallback to CPMAddPackage +function(CPMFindPackage) + set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + set(downloadPackage ${CPM_DOWNLOAD_ALL}) + if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME}) + set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + endif() + if(downloadPackage) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + if(CPM_PACKAGE_ALREADY_ADDED) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(NOT CPM_PACKAGE_FOUND) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + endif() + +endfunction() + +# checks if a package has been added before +function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION) + if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) + CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) + if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}") + message( + WARNING + "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})." + ) + endif() + cpm_get_fetch_properties(${CPM_ARGS_NAME}) + set(${CPM_ARGS_NAME}_ADDED NO) + set(CPM_PACKAGE_ALREADY_ADDED + YES + PARENT_SCOPE + ) + cpm_export_variables(${CPM_ARGS_NAME}) + else() + set(CPM_PACKAGE_ALREADY_ADDED + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of +# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted +# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3 +function(cpm_parse_add_package_single_arg arg outArgs) + # Look for a scheme + if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$") + string(TOLOWER "${CMAKE_MATCH_1}" scheme) + set(uri "${CMAKE_MATCH_2}") + + # Check for CPM-specific schemes + if(scheme STREQUAL "gh") + set(out "GITHUB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "gl") + set(out "GITLAB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "bb") + set(out "BITBUCKET_REPOSITORY;${uri}") + set(packageType "git") + # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine + # type + elseif(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Fall back to a URL + set(out "URL;${arg}") + set(packageType "archive") + + # We could also check for SVN since FetchContent supports it, but SVN is so rare these days. + # We just won't bother with the additional complexity it will induce in this function. SVN is + # done by multi-arg + endif() + else() + if(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Give up + message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'") + endif() + endif() + + # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs + # containing '@' can be used + string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}") + + # Parse the rest according to package type + if(packageType STREQUAL "git") + # For git repos we interpret #... as a tag or branch or commit hash + string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}") + elseif(packageType STREQUAL "archive") + # For archives we interpret #... as a URL hash. + string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}") + # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url + # should do this at a later point + else() + # We should never get here. This is an assertion and hitting it means there's a bug in the code + # above. A packageType was set, but not handled by this if-else. + message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'") + endif() + + set(${outArgs} + ${out} + PARENT_SCOPE + ) +endfunction() + +# Check that the working directory for a git repo is clean +function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean) + + find_package(Git REQUIRED) + + if(NOT GIT_EXECUTABLE) + # No git executable, assume directory is clean + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + # check for uncommitted changes + execute_process( + COMMAND ${GIT_EXECUTABLE} status --porcelain + RESULT_VARIABLE resultGitStatus + OUTPUT_VARIABLE repoStatus + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + WORKING_DIRECTORY ${repoPath} + ) + if(resultGitStatus) + # not supposed to happen, assume clean anyway + message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed") + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + if(NOT "${repoStatus}" STREQUAL "") + set(${isClean} + FALSE + PARENT_SCOPE + ) + return() + endif() + + # check for committed changes + execute_process( + COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag} + RESULT_VARIABLE resultGitDiff + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET + WORKING_DIRECTORY ${repoPath} + ) + + if(${resultGitDiff} EQUAL 0) + set(${isClean} + TRUE + PARENT_SCOPE + ) + else() + set(${isClean} + FALSE + PARENT_SCOPE + ) + endif() + +endfunction() + +# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload +# FetchContent calls. As these are internal cmake properties, this method should be used carefully +# and may need modification in future CMake versions. Source: +# https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152 +function(cpm_override_fetchcontent contentName) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "") + if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + string(TOLOWER ${contentName} contentNameLower) + set(prefix "_FetchContent_${contentNameLower}") + + set(propertyName "${prefix}_sourceDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") + + set(propertyName "${prefix}_binaryDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") + + set(propertyName "${prefix}_populated") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} TRUE) +endfunction() + +# Download and add a package from source +function(CPMAddPackage) + cpm_set_policies() + + list(LENGTH ARGN argnLength) + if(argnLength EQUAL 1) + cpm_parse_add_package_single_arg("${ARGN}" ARGN) + + # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM + set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") + endif() + + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + DOWNLOAD_COMMAND + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + ) + + set(multiValueArgs URL OPTIONS) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") + + # Set default values for arguments + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + if(CPM_ARGS_DOWNLOAD_ONLY) + set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) + else() + set(DOWNLOAD_ONLY NO) + endif() + + if(DEFINED CPM_ARGS_GITHUB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git") + endif() + + if(DEFINED CPM_ARGS_GIT_REPOSITORY) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY}) + if(NOT DEFINED CPM_ARGS_GIT_TAG) + set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) + endif() + + # If a name wasn't provided, try to infer it from the git repo + if(NOT DEFINED CPM_ARGS_NAME) + cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME) + endif() + endif() + + set(CPM_SKIP_FETCH FALSE) + + if(DEFINED CPM_ARGS_GIT_TAG) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) + # If GIT_SHALLOW is explicitly specified, honor the value. + if(DEFINED CPM_ARGS_GIT_SHALLOW) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW}) + endif() + endif() + + if(DEFINED CPM_ARGS_URL) + # If a name or version aren't provided, try to infer them from the URL + list(GET CPM_ARGS_URL 0 firstUrl) + cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl) + # If we fail to obtain name and version from the first URL, we could try other URLs if any. + # However multiple URLs are expected to be quite rare, so for now we won't bother. + + # If the caller provided their own name and version, they trump the inferred ones. + if(NOT DEFINED CPM_ARGS_NAME) + set(CPM_ARGS_NAME ${nameFromUrl}) + endif() + if(NOT DEFINED CPM_ARGS_VERSION) + set(CPM_ARGS_VERSION ${verFromUrl}) + endif() + + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}") + endif() + + # Check for required arguments + + if(NOT DEFINED CPM_ARGS_NAME) + message( + FATAL_ERROR + "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'" + ) + endif() + + # Check if package has been added before + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + if(CPM_PACKAGE_ALREADY_ADDED) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for manual overrides + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "") + set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE}) + set(CPM_${CPM_ARGS_NAME}_SOURCE "") + CPMAddPackage( + NAME "${CPM_ARGS_NAME}" + SOURCE_DIR "${PACKAGE_SOURCE}" + EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}" + SYSTEM "${CPM_ARGS_SYSTEM}" + OPTIONS "${CPM_ARGS_OPTIONS}" + SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}" + DOWNLOAD_ONLY "${DOWNLOAD_ONLY}" + FORCE True + ) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for available declaration + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "") + set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}}) + set(CPM_DECLARATION_${CPM_ARGS_NAME} "") + CPMAddPackage(${declaration}) + cpm_export_variables(${CPM_ARGS_NAME}) + # checking again to ensure version and option compatibility + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + return() + endif() + + if(NOT CPM_ARGS_FORCE) + if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(CPM_PACKAGE_FOUND) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + if(CPM_LOCAL_PACKAGES_ONLY) + message( + SEND_ERROR + "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})" + ) + endif() + endif() + endif() + + CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}") + + if(DEFINED CPM_ARGS_GIT_TAG) + set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}") + else() + set(PACKAGE_INFO "${CPM_ARGS_VERSION}") + endif() + + if(DEFINED FETCHCONTENT_BASE_DIR) + # respect user's FETCHCONTENT_BASE_DIR if set + set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR}) + else() + set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps) + endif() + + if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) + if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR}) + # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work + # for relative paths. + get_filename_component( + source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR} + ) + else() + set(source_directory ${CPM_ARGS_SOURCE_DIR}) + endif() + if(NOT EXISTS ${source_directory}) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild") + endif() + elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) + list(SORT origin_parameters) + if(CPM_USE_NAMED_CACHE_DIRECTORIES) + string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) + else() + string(SHA1 origin_hash "${origin_parameters}") + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) + endif() + # Expand `download_directory` relative path. This is important because EXISTS doesn't work for + # relative paths. + get_filename_component(download_directory ${download_directory} ABSOLUTE) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory}) + + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock) + endif() + + if(EXISTS ${download_directory}) + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} "${download_directory}" + "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" + ) + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + + if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS)) + # warn if cache has been changed since checkout + cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN) + if(NOT ${IS_CLEAN}) + message( + WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty" + ) + endif() + endif() + + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}") + + # As the source dir is already cached/populated, we override the call to FetchContent. + set(CPM_SKIP_FETCH TRUE) + cpm_override_fetchcontent( + "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" + ) + + else() + # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but + # it should guarantee no commit hash get mis-detected. + if(NOT DEFINED CPM_ARGS_GIT_SHALLOW) + cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH) + if(NOT ${IS_HASH}) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE) + endif() + endif() + + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild) + set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}") + endif() + endif() + + cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")") + + if(CPM_PACKAGE_LOCK_ENABLED) + if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK) + cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + elseif(CPM_ARGS_SOURCE_DIR) + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory") + else() + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + endif() + endif() + + cpm_message( + STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})" + ) + + if(NOT CPM_SKIP_FETCH) + cpm_declare_fetch( + "${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}" "${PACKAGE_INFO}" "${CPM_ARGS_UNPARSED_ARGUMENTS}" + ) + cpm_fetch_package("${CPM_ARGS_NAME}" populated) + if(CPM_SOURCE_CACHE AND download_directory) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + if(${populated}) + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + endif() + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + endif() + + set(${CPM_ARGS_NAME}_ADDED YES) + cpm_export_variables("${CPM_ARGS_NAME}") +endfunction() + +# Fetch a previously declared package +macro(CPMGetPackage Name) + if(DEFINED "CPM_DECLARATION_${Name}") + CPMAddPackage(NAME ${Name}) + else() + message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available") + endif() +endmacro() + +# export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set +macro(cpm_export_variables name) + set(${name}_SOURCE_DIR + "${${name}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${name}_BINARY_DIR + "${${name}_BINARY_DIR}" + PARENT_SCOPE + ) + set(${name}_ADDED + "${${name}_ADDED}" + PARENT_SCOPE + ) + set(CPM_LAST_PACKAGE_NAME + "${name}" + PARENT_SCOPE + ) +endmacro() + +# declares a package, so that any call to CPMAddPackage for the package name will use these +# arguments instead. Previous declarations will not be overridden. +macro(CPMDeclarePackage Name) + if(NOT DEFINED "CPM_DECLARATION_${Name}") + set("CPM_DECLARATION_${Name}" "${ARGN}") + endif() +endmacro() + +function(cpm_add_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n") + endif() +endfunction() + +function(cpm_add_comment_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} + "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n" + ) + endif() +endfunction() + +# includes the package lock file if it exists and creates a target `cpm-update-package-lock` to +# update it +macro(CPMUsePackageLock file) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE) + if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + endif() + if(NOT TARGET cpm-update-package-lock) + add_custom_target( + cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} + ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH} + ) + endif() + set(CPM_PACKAGE_LOCK_ENABLED true) + endif() +endmacro() + +# registers a package that has been added to CPM +function(CPMRegisterPackage PACKAGE VERSION) + list(APPEND CPM_PACKAGES ${PACKAGE}) + set(CPM_PACKAGES + ${CPM_PACKAGES} + CACHE INTERNAL "" + ) + set("CPM_PACKAGE_${PACKAGE}_VERSION" + ${VERSION} + CACHE INTERNAL "" + ) +endfunction() + +# retrieve the current version of the package to ${OUTPUT} +function(CPMGetPackageVersion PACKAGE OUTPUT) + set(${OUTPUT} + "${CPM_PACKAGE_${PACKAGE}_VERSION}" + PARENT_SCOPE + ) +endfunction() + +# declares a package in FetchContent_Declare +function(cpm_declare_fetch PACKAGE VERSION INFO) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)") + return() + endif() + + FetchContent_Declare(${PACKAGE} ${ARGN}) +endfunction() + +# returns properties for a package previously defined by cpm_declare_fetch +function(cpm_get_fetch_properties PACKAGE) + if(${CPM_DRY_RUN}) + return() + endif() + + set(${PACKAGE}_SOURCE_DIR + "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}" + PARENT_SCOPE + ) +endfunction() + +function(cpm_store_fetch_properties PACKAGE source_dir binary_dir) + if(${CPM_DRY_RUN}) + return() + endif() + + set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR + "${source_dir}" + CACHE INTERNAL "" + ) + set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR + "${binary_dir}" + CACHE INTERNAL "" + ) +endfunction() + +# adds a package as a subdirectory if viable, according to provided options +function( + cpm_add_subdirectory + PACKAGE + DOWNLOAD_ONLY + SOURCE_DIR + BINARY_DIR + EXCLUDE + SYSTEM + OPTIONS +) + + if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt) + set(addSubdirectoryExtraArgs "") + if(EXCLUDE) + list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL) + endif() + if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") + # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM + list(APPEND addSubdirectoryExtraArgs SYSTEM) + endif() + if(OPTIONS) + foreach(OPTION ${OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + endif() + set(CPM_OLD_INDENT "${CPM_INDENT}") + set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") + add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs}) + set(CPM_INDENT "${CPM_OLD_INDENT}") + endif() +endfunction() + +# downloads a previously declared package via FetchContent and exports the variables +# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope +function(cpm_fetch_package PACKAGE populated) + set(${populated} + FALSE + PARENT_SCOPE + ) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)") + return() + endif() + + FetchContent_GetProperties(${PACKAGE}) + + string(TOLOWER "${PACKAGE}" lower_case_name) + + if(NOT ${lower_case_name}_POPULATED) + FetchContent_Populate(${PACKAGE}) + set(${populated} + TRUE + PARENT_SCOPE + ) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} + ) + + set(${PACKAGE}_SOURCE_DIR + ${${lower_case_name}_SOURCE_DIR} + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + ${${lower_case_name}_BINARY_DIR} + PARENT_SCOPE + ) +endfunction() + +# splits a package option +function(cpm_parse_option OPTION) + string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") + string(LENGTH "${OPTION}" OPTION_LENGTH) + string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) + if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) + # no value for key provided, assume user wants to set option to "ON" + set(OPTION_VALUE "ON") + else() + math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") + string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) + endif() + set(OPTION_KEY + "${OPTION_KEY}" + PARENT_SCOPE + ) + set(OPTION_VALUE + "${OPTION_VALUE}" + PARENT_SCOPE + ) +endfunction() + +# guesses the package version from a git tag +function(cpm_get_version_from_git_tag GIT_TAG RESULT) + string(LENGTH ${GIT_TAG} length) + if(length EQUAL 40) + # GIT_TAG is probably a git hash + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + endif() +endfunction() + +# guesses if the git tag is a commit hash or an actual tag or a branch name. +function(cpm_is_git_tag_commit_hash GIT_TAG RESULT) + string(LENGTH "${GIT_TAG}" length) + # full hash has 40 characters, and short hash has at least 7 characters. + if(length LESS 7 OR length GREATER 40) + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$") + set(${RESULT} + 1 + PARENT_SCOPE + ) + else() + set(${RESULT} + 0 + PARENT_SCOPE + ) + endif() + endif() +endfunction() + +function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT) + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + DOWNLOAD_COMMAND + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + ) + set(multiValueArgs OPTIONS) + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(oneArgName ${oneValueArgs}) + if(DEFINED CPM_ARGS_${oneArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + if(${oneArgName} STREQUAL "SOURCE_DIR") + string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName} + ${CPM_ARGS_${oneArgName}} + ) + endif() + string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n") + endif() + endforeach() + foreach(multiArgName ${multiValueArgs}) + if(DEFINED CPM_ARGS_${multiArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n") + foreach(singleOption ${CPM_ARGS_${multiArgName}}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n") + endforeach() + endif() + endforeach() + + if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "") + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ") + foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS}) + string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}") + endforeach() + string(APPEND PRETTY_OUT_VAR "\n") + endif() + + set(${OUT_VAR} + ${PRETTY_OUT_VAR} + PARENT_SCOPE + ) + +endfunction() diff --git a/NativeLibs/yasio/1k/dm/CMakeLists.txt b/NativeLibs/yasio/1k/dm/CMakeLists.txt new file mode 100644 index 0000000..b30e293 --- /dev/null +++ b/NativeLibs/yasio/1k/dm/CMakeLists.txt @@ -0,0 +1,42 @@ + +cmake_minimum_required(VERSION 3.23) + +project(dm) + +find_program(PWSH_PROG NAMES pwsh powershell NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) + +message(STATUS "CMAKE_C_COMPILER=${CMAKE_C_COMPILER}") +message(STATUS "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") +message(STATUS "CMAKE_C_FLAGS=${CMAKE_C_FLAGS}") +message(STATUS "CMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}") +message(STATUS "CMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS}") +message(STATUS "CMAKE_SYSROOT=${CMAKE_SYSROOT}") + +set(cross_flags "") +if(ANDROID) + message(STATUS "ANDROID_NATIVE_API_LEVEL=${ANDROID_NATIVE_API_LEVEL}") + if(CMAKE_ANDROID_ARCH_ABI MATCHES "arm64-v8a") + set(cross_flags "-target aarch64-none-linux-android${ANDROID_NATIVE_API_LEVEL}") + elseif(CMAKE_ANDROID_ARCH_ABI MATCHES "x86_64") + set(cross_flags "-target x86_64-none-linux-android${ANDROID_NATIVE_API_LEVEL}") + elseif(CMAKE_ANDROID_ARCH_ABI MATCHES "x86") + set(cross_flags "-target x86-none-linux-android${ANDROID_NATIVE_API_LEVEL}") + else() + set(cross_flags "-target arm-none-linux-android${ANDROID_NATIVE_API_LEVEL}") + endif() +elseif(APPLE) + if(NOT CMAKE_OSX_ARCHITECTURES) + set(CMAKE_OSX_ARCHITECTURES ${CMAKE_HOST_SYSTEM_PROCESSOR}) + endif() + if(CMAKE_OSX_ARCHITECTURES MATCHES "arm64") + set(cross_flags "-arch arm64") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") + set(cross_flags "-arch x86_64") + endif() +endif() + +if(NOT ${CMAKE_C_COMPILER} MATCHES ".*cl\.exe" OR "${MSVC_VERSION}" GREATER_EQUAL 1928) + execute_process(COMMAND ${PWSH_PROG} "${CMAKE_CURRENT_SOURCE_DIR}/dm.ps1" "${CMAKE_C_COMPILER}" "${CMAKE_C_FLAGS} ${cross_flags}") +endif() + +add_library(dm dm.c) \ No newline at end of file diff --git a/NativeLibs/yasio/1k/dm/dm.c b/NativeLibs/yasio/1k/dm/dm.c new file mode 100644 index 0000000..5c71dcf --- /dev/null +++ b/NativeLibs/yasio/1k/dm/dm.c @@ -0,0 +1,4 @@ +long double dm2x(long double v) +{ + return v * 10.33; +} \ No newline at end of file diff --git a/NativeLibs/yasio/1k/dm/dm.ps1 b/NativeLibs/yasio/1k/dm/dm.ps1 new file mode 100644 index 0000000..ab6c035 --- /dev/null +++ b/NativeLibs/yasio/1k/dm/dm.ps1 @@ -0,0 +1,17 @@ +# need plan args to avoid powershell preprocess args unexpected behavior +$cc = $args[0] +$cflags = $args[1] + +$IsWin = $IsWindows -or ("$env:OS" -eq 'Windows_NT') + +echo "cflags=${cflags}" + +if($cflags) { $cflags = Invoke-Expression -Command "echo $cflags" } + +if($cc.EndsWith('cl.exe')) { + # vs2019+ support: + &$cc /EP /Zc:preprocessor /PD $cflags (Join-Path $PSScriptRoot 'dm.c') +} else { + Write-Host "dump command: <<$cc -E -dM $cflags ->>" + echo ''| &$cc -E -dM $cflags - +} diff --git a/NativeLibs/yasio/1k/extensions.ps1 b/NativeLibs/yasio/1k/extensions.ps1 new file mode 100644 index 0000000..94ba6d3 --- /dev/null +++ b/NativeLibs/yasio/1k/extensions.ps1 @@ -0,0 +1,221 @@ +# The System.Version compare not relex: [Version]'1.0' -eq [Version]'1.0.0' == false +# So provide a spec VersionEx make [VersionEx]'1.0' -eq [VersionEx]'1.0.0' == true available +if (-not ([System.Management.Automation.PSTypeName]'System.VersionEx').Type) { + + Add-Type -TypeDefinition @" + +namespace System +{ + public sealed class VersionEx : ICloneable, IComparable + { + int _Major = 0; + int _Minor = 0; + int _Build = 0; + int _Revision = 0; + + public int Minor { get { return _Major; } } + + public int Major { get { return _Minor; } } + + public int Build { get { return _Build; } } + + public int Revision { get { return _Revision; } } + + int DefaultFormatFieldCount { get { return (_Build > 0 || _Revision > 0) ? (_Revision > 0 ? 4 : 3) : 2; } } + + public VersionEx() { } + + public VersionEx(string version) + { + var v = Parse(version); + _Major = v.Major; + _Minor = v.Minor; + _Build = v.Build; + _Revision = v.Revision; + } + + public VersionEx(System.Version version) { + _Major = version.Major; + _Minor = version.Minor; + _Build = Math.Max(version.Build, 0); + _Revision = Math.Max(version.Revision, 0); + } + + public VersionEx(int major, int minor, int build, int revision) + { + _Major = major; + _Minor = minor; + _Build = build; + _Revision = revision; + } + + public static VersionEx Parse(string input) + { + var versionNums = input.Split('.'); + int major = 0; + int minor = 0; + int build = 0; + int revision = 0; + for (int i = 0; i < versionNums.Length; ++i) + { + switch (i) + { + case 0: + major = int.Parse(versionNums[i]); + break; + case 1: + minor = int.Parse(versionNums[i]); + break; + case 2: + build = int.Parse(versionNums[i]); + break; + case 3: + revision = int.Parse(versionNums[i]); + break; + } + } + return new VersionEx(major, minor, build, revision); + } + + public static bool TryParse(string input, out VersionEx result) + { + try + { + result = VersionEx.Parse(input); + return true; + } + catch (Exception) + { + result = null; + return false; + } + } + + public object Clone() + { + return new VersionEx(Major, Minor, Build, Revision); + } + + public int CompareTo(object obj) + { + if (obj is VersionEx) + { + return CompareTo((VersionEx)obj); + } + else if (obj is Version) + { + var rhs = (Version)obj; + return _Major != rhs.Major ? (_Major > rhs.Major ? 1 : -1) : + _Minor != rhs.Minor ? (_Minor > rhs.Minor ? 1 : -1) : + _Build != rhs.Build ? (_Build > rhs.Build ? 1 : -1) : + _Revision != rhs.Revision ? (_Revision > rhs.Revision ? 1 : -1) : + 0; + } + else return 1; + } + + public int CompareTo(VersionEx obj) + { + return + ReferenceEquals(obj, this) ? 0 : + ReferenceEquals(obj, null) ? 1 : + _Major != obj._Major ? (_Major > obj._Major ? 1 : -1) : + _Minor != obj._Minor ? (_Minor > obj._Minor ? 1 : -1) : + _Build != obj._Build ? (_Build > obj._Build ? 1 : -1) : + _Revision != obj._Revision ? (_Revision > obj._Revision ? 1 : -1) : + 0; + } + + public bool Equals(VersionEx obj) + { + return CompareTo(obj) == 0; + } + + public override bool Equals(object obj) + { + return CompareTo(obj) == 0; + } + + public override string ToString() + { + return ToString(DefaultFormatFieldCount); + } + + public string ToString(int fieldCount) + { + switch (fieldCount) + { + case 2: + return string.Format("{0}.{1}", _Major, _Minor); + case 3: + return string.Format("{0}.{1}.{2}", _Major, _Minor, _Build); + case 4: + return string.Format("{0}.{1}.{2}.{3}", _Major, _Minor, _Build, _Revision); + default: + return "0.0.0.0"; + } + } + + public override int GetHashCode() + { + // Let's assume that most version numbers will be pretty small and just + // OR some lower order bits together. + + int accumulator = 0; + + accumulator |= (_Major & 0x0000000F) << 28; + accumulator |= (_Minor & 0x000000FF) << 20; + accumulator |= (_Build & 0x000000FF) << 12; + accumulator |= (_Revision & 0x00000FFF); + + return accumulator; + } + + public static bool operator ==(VersionEx v1, VersionEx v2) + { + return v1.Equals(v2); + } + + public static bool operator !=(VersionEx v1, VersionEx v2) + { + return !v1.Equals(v2); + } + + public static bool operator <(VersionEx v1, VersionEx v2) + { + return v1.CompareTo(v2) < 0; + } + + public static bool operator >(VersionEx v1, VersionEx v2) + { + return v1.CompareTo(v2) > 0; + } + + public static bool operator <=(VersionEx v1, VersionEx v2) + { + return v1.CompareTo(v2) <= 0; + } + + public static bool operator >=(VersionEx v1, VersionEx v2) + { + return v1.CompareTo(v2) >= 0; + } + } + + public static class ExtensionMethods + { + public static string TrimLast(this Management.Automation.PSObject thiz, string separator) + { + var str = thiz.BaseObject as string; + var index = str.LastIndexOf(separator); + if (index != -1) + return str.Substring(0, index); + return str; + } + } +} +"@ + +$TrimLastMethod = [ExtensionMethods].GetMethod('TrimLast') +Update-TypeData -TypeName System.String -MemberName TrimLast -MemberType CodeMethod -Value $TrimLastMethod +} diff --git a/NativeLibs/yasio/1k/fetch.cmake b/NativeLibs/yasio/1k/fetch.cmake index 391bb53..6fa0c08 100644 --- a/NativeLibs/yasio/1k/fetch.cmake +++ b/NativeLibs/yasio/1k/fetch.cmake @@ -4,29 +4,54 @@ # _1kfetch_cache_dir # _1kfetch_manifest # +cmake_minimum_required(VERSION 3.23) ### 1kdist url -find_program(PWSH_COMMAND NAMES pwsh powershell NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) +find_program(PWSH_PROG NAMES pwsh powershell NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) +find_program(GIT_PROG NAMES git NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) function(_1kfetch_init) - execute_process(COMMAND ${PWSH_COMMAND} ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/fetchurl.ps1 + if(NOT _1kfetch_cache_dir) + file(REAL_PATH "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../cache" _1kfetch_cache_dir) + set(_1kfetch_cache_dir "${_1kfetch_cache_dir}" CACHE STRING "" FORCE) + endif() + if(NOT _1kfetch_manifest) + file(REAL_PATH "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../manifest.json" _1kfetch_manifest) + set(_1kfetch_manifest "${_1kfetch_manifest}" CACHE STRING "" FORCE) + endif() + + if(NOT EXISTS ${PWSH_PROG}) # try again + unset(PWSH_PROG CACHE) + find_program(PWSH_PROG NAMES pwsh powershell NO_PACKAGE_ROOT_PATH NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH NO_CMAKE_FIND_ROOT_PATH) + endif() + + execute_process(COMMAND ${PWSH_PROG} ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/resolv-uri.ps1 -name "1kdist" - -cfg ${_1kfetch_manifest} + -manifest ${_1kfetch_manifest} OUTPUT_VARIABLE _1kdist_url + RESULT_VARIABLE _1kdist_error ) - string(REPLACE "#" ";" _1kdist_url ${_1kdist_url}) - list(GET _1kdist_url 0 _1kdist_base_url) - list(GET _1kdist_url 1 _1kdist_ver) - set(_1kdist_base_url "${_1kdist_base_url}/v${_1kdist_ver}" PARENT_SCOPE) - set(_1kdist_ver ${_1kdist_ver} PARENT_SCOPE) + + if(_1kdist_url) + string(REPLACE "#" ";" _1kdist_url ${_1kdist_url}) + list(GET _1kdist_url 0 _1kdist_base_url) + list(GET _1kdist_url 1 _1kdist_ver) + set(_1kdist_base_url "${_1kdist_base_url}/${_1kdist_ver}" PARENT_SCOPE) + set(_1kdist_ver ${_1kdist_ver} PARENT_SCOPE) + else() + message(WARNING "Resolve 1kdist uri fail, ${_1kdist_error}, the _1kfetch_dist will not work") + endif() endfunction() # fetch prebuilt from 1kdist # param package_name function(_1kfetch_dist package_name) - set(_prebuilt_root ${CMAKE_CURRENT_LIST_DIR}/_d) + set(_prebuilt_root ${CMAKE_CURRENT_LIST_DIR}/_x) + if(_1KFETCH_UPGRADE AND IS_DIRECTORY ${_prebuilt_root}) + file(REMOVE_RECURSE ${_prebuilt_root}) + endif() if(NOT IS_DIRECTORY ${_prebuilt_root}) - set (package_store "${_1kfetch_cache_dir}/1kdist/v${_1kdist_ver}/${package_name}.zip") + set (package_store "${_1kfetch_cache_dir}/1kdist/${_1kdist_ver}/${package_name}.zip") if (NOT EXISTS ${package_store}) set (package_url "${_1kdist_base_url}/${package_name}.zip") message(AUTHOR_WARNING "Downloading ${package_url}") @@ -34,43 +59,199 @@ function(_1kfetch_dist package_name) list(GET _status 0 status_code) list(GET _status 1 status_string) if(NOT status_code EQUAL 0) + file(REMOVE ${package_store}) message(FATAL_ERROR "Download ${package_url} fail, ${status_string}, logs: ${_logs}") endif() endif() file(ARCHIVE_EXTRACT INPUT ${package_store} DESTINATION ${CMAKE_CURRENT_LIST_DIR}/) - file(RENAME ${CMAKE_CURRENT_LIST_DIR}/${package_name} ${_prebuilt_root}) + if (IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${package_name}) + file(RENAME ${CMAKE_CURRENT_LIST_DIR}/${package_name} ${_prebuilt_root}) + else() # download may fail + file(REMOVE ${package_store}) + message(FATAL_ERROR "The package ${package_store} is malformed, please try again!") + endif() endif() # set platform specific path, PLATFORM_NAME provided by user: win32,winrt,mac,ios,android,tvos,watchos,linux - set(_prebuilt_lib_dir "${_prebuilt_root}/lib/${PLATFORM_NAME}") - if(ANDROID OR WIN32) - set(_prebuilt_lib_dir "${_prebuilt_lib_dir}/${ARCH_ALIAS}") + if(APPLE) # since 1kiss_dist v80+, xcframework don't require platform spec sub folder + set(_prebuilt_lib_dir "${_prebuilt_root}/lib") + else() + set(_prebuilt_lib_dir "${_prebuilt_root}/lib/${PLATFORM_NAME}") + if(ANDROID OR WIN32) + set(_prebuilt_lib_dir "${_prebuilt_lib_dir}/${ARCH_ALIAS}") + endif() endif() set(${package_name}_INC_DIR ${_prebuilt_root}/include PARENT_SCOPE) set(${package_name}_LIB_DIR ${_prebuilt_lib_dir} PARENT_SCOPE) endfunction() -# params: name, url -function(_1kfetch name) - set(_pkg_store "${_1kfetch_cache_dir}/${name}") - execute_process(COMMAND ${PWSH_COMMAND} ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/fetch.ps1 - -name "${name}" - -dest "${_pkg_store}" - -cfg ${_1kfetch_manifest} - RESULT_VARIABLE _errorcode +function(_1kfetch uri) + set(oneValueArgs NAME REV) + cmake_parse_arguments(opt "" "${oneValueArgs}" "" ${ARGN}) + + _1kparse_name(${uri} "${opt_NAME}") + + set(_pkg_store "${_1kfetch_cache_dir}/${_pkg_name}") + + get_property(_fetched GLOBAL PROPERTY "${_pkg_name}_fetched") + if (NOT _fetched) + set(_fetch_args + -uri "${uri}" + -prefix "${_1kfetch_cache_dir}" + -manifest "${_1kfetch_manifest}" + -name "${_pkg_name}" ) - if (_errorcode) - message(FATAL_ERROR "aborted") + # rev: the explicit rev to checkout, i.e. git release tag name + if(opt_REV) + list(APPEND _fetch_args -rev ${opt_REV}) + endif() + + if(_1KFETCH_UPGRADE) + list(APPEND _fetch_args -pull_branch) + endif() + + execute_process(COMMAND ${PWSH_PROG} ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/fetch.ps1 + ${_fetch_args} + RESULT_VARIABLE _errorcode + ) + if (_errorcode) + message(FATAL_ERROR "fetch content ${uri} failed") + endif() + + set_property(GLOBAL PROPERTY "${_pkg_name}_fetched" TRUE) + endif() + + set(${_pkg_name}_SOURCE_DIR ${_pkg_store} PARENT_SCOPE) + set(source_dir ${_pkg_store} PARENT_SCOPE) +endfunction() + +# developing, not available yet +function(_1kfetch_fast uri) + _1kperf_start("_1kfetch: ${uri}") + + set(oneValueArgs NAME REV) + cmake_parse_arguments(opt "" "${oneValueArgs}" "" ${ARGN}) + + _1kparse_name(${uri} "${opt_NAME}") + + set(_pkg_store "${_1kfetch_cache_dir}/${_pkg_name}") + + set(_sentry_file "${_pkg_store}/_1kiss") + + if(NOT _manifest_conf) + file(READ "${_1kfetch_manifest}" _manifest_conf) + endif() + string(JSON _url GET "${_manifest_conf}" "mirrors" "github" "${_pkg_name}") + string(JSON _version GET "${_manifest_conf}" "versions" "${_pkg_name}") + string(PREPEND _url "https://github.com/") + if(NOT EXISTS "${_sentry_file}") + execute_process(COMMAND ${GIT_PROG} clone --progress ${_url} "${_pkg_store}" RESULT_VARIABLE _errorcode) + file(WRITE "${_sentry_file}" "ver: ${_version}") + endif() + + if(EXISTS "${_sentry_file}") + execute_process(COMMAND ${GIT_PROG} -C ${_pkg_store} checkout ${_version} RESULT_VARIABLE _errorcode) + if(_errorcode) + execute_process(COMMAND ${GIT_PROG} -C ${_pkg_store} checkout v${_version} RESULT_VARIABLE _errorcode) + endif() + else() + message(FATAL_ERROR "fetch repo ${uri} fail, try again") + endif() + + set(${_pkg_name}_SOURCE_DIR ${_pkg_store} PARENT_SCOPE) + set(source_dir ${_pkg_store} PARENT_SCOPE) + + _1kperf_end("_1kfetch") +endfunction() + +# simple cmake pkg management: +# for example: _1kadd_pkg("gh:yasio/yasio#4.2.1") +function(_1kadd_pkg uri) + set(optValueArgs EXCLUDE_FROM_ALL) + set(oneValueArgs BINARY_DIR NAME) + set(multiValueArgs OPTIONS) + cmake_parse_arguments(opt "${optValueArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + _1kparse_name(${uri} "${opt_NAME}") + + if(NOT TARGET ${_pkg_name}) + _1kfetch(${uri} ${ARGN} NAME ${_pkg_name}) + + foreach(OPTION ${opt_OPTIONS}) + _1kparse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}" CACHE BOOL "" FORCE) + endforeach() + + if(opt_BINARY_DIR) + set(binary_dir "${opt_BINARY_DIR}/${_pkg_name}") + else() + set(binary_dir "${CMAKE_BINARY_DIR}/1kiss/${_pkg_name}") + endif() + + if (opt_EXCLUDE_FROM_ALL) + add_subdirectory(${source_dir} ${binary_dir} EXCLUDE_FROM_ALL) + else() + add_subdirectory(${source_dir} ${binary_dir}) + endif() endif() - set(${name}_SOURCE_DIR ${_pkg_store} PARENT_SCOPE) endfunction() function(_1klink src dest) file(TO_NATIVE_PATH "${src}" _srcDir) file(TO_NATIVE_PATH "${dest}" _dstDir) - execute_process(COMMAND ${PWSH_COMMAND} ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/fsync.ps1 -s "${_srcDir}" -d "${_dstDir}" -l 1) + execute_process(COMMAND ${PWSH_PROG} ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/fsync.ps1 -s "${_srcDir}" -d "${_dstDir}" -l 1) endfunction() -if(PWSH_COMMAND) +function(_1kparse_option OPTION) + string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") + string(LENGTH "${OPTION}" OPTION_LENGTH) + string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) + if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) + # no value for key provided, assume user wants to set option to "ON" + set(OPTION_VALUE "ON") + else() + math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") + string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) + endif() + set(OPTION_KEY + "${OPTION_KEY}" + PARENT_SCOPE + ) + set(OPTION_VALUE + "${OPTION_VALUE}" + PARENT_SCOPE + ) +endfunction() + +macro(_1kparse_name uri opt_NAME) + if(opt_NAME) + set(_pkg_name ${opt_NAME}) + else() + set(_trimmed_uri "") + # parse pkg name for pkg_store due to we can't get from execute_process properly + string(REGEX REPLACE "#.*" "" _trimmed_uri "${uri}") + get_filename_component(_pkg_name ${_trimmed_uri} NAME_WE) + set(_pkg_name ${_pkg_name}) + endif() +endmacro() + +macro(_1kperf_start tag) + string(TIMESTAMP _current_sec "%s" UTC) + string(TIMESTAMP _current_usec "%f" UTC) + math(EXPR _fetch_start_msec "${_current_sec} * 1000 + ${_current_usec} / 1000" OUTPUT_FORMAT DECIMAL) + message(STATUS "[${_fetch_start_msec}ms][1kperf] start of ${tag} ..." ) +endmacro() + +macro(_1kperf_end tag) + string(TIMESTAMP _current_sec "%s" UTC) + string(TIMESTAMP _current_usec "%f" UTC) + math(EXPR _fetch_end_msec "${_current_sec} * 1000 + ${_current_usec} / 1000" OUTPUT_FORMAT DECIMAL) + math(EXPR _fetch_cost_msec "${_fetch_end_msec} - ${_fetch_start_msec}") + message(STATUS "[${_fetch_end_msec}ms][1kperf] end of ${tag}, cost: ${_fetch_cost_msec}ms" ) +endmacro() + +if(PWSH_PROG) _1kfetch_init() +else() + message(WARNING "fetch.cmake: PowerShell is missing, the fetch functions not work, please install from https://github.com/PowerShell/PowerShell/releases") endif() diff --git a/NativeLibs/yasio/1k/fetch.ps1 b/NativeLibs/yasio/1k/fetch.ps1 new file mode 100644 index 0000000..411c38f --- /dev/null +++ b/NativeLibs/yasio/1k/fetch.ps1 @@ -0,0 +1,274 @@ +# fetch pkg by url or manifest.json path +param( + $uri, # the pkg uri + $prefix, # the prefix to store + $manifest_file = $null, + $name = $null, + $version = $null, # version hint + $revision = $null, # revision hint + [switch]$pull_branch +) + +# content of _1kiss with yaml format +# ver: 1.0 +# branch: 1.x +# commits: 2802 +# rev: 29b0b28 + +Set-Alias println Write-Host + +if (!$uri -or !$prefix) { + throw 'fetch.ps1: missing parameters' +} + +function download_file($uri, $out) { + if (Test-Path $out -PathType Leaf) { return } + println "Downloading $uri to $out ..." + Invoke-WebRequest -Uri $uri -OutFile $out +} + +function mkdirs($path) { + if (!(Test-Path $path -PathType Container)) { + New-Item $path -ItemType Directory 1>$null + } +} + +# ensure cachedir +$cache_dir = Join-Path (Resolve-Path $PSScriptRoot/..).Path 'cache' +if (!(Test-Path $cache_dir -PathType Container)) { + mkdirs $cache_dir +} + +function fetch_repo($url, $name, $dest, $ext) { + if ($ext -eq '.git') { + git clone --progress $url $dest | Out-Host + } + else { + $out = Join-Path $cache_dir "$Script:url_pkg_name$ext" + download_file $url $out + try { + if ($ext -eq '.zip') { + Expand-Archive -Path $out -DestinationPath $prefix -Force + } + elseif ($ext -match '\.tar(\..*)?$') { + tar xvf "$out" -C $prefix + } + } + catch { + Remove-Item $out -Force + throw "fetch.ps1: extract $out failed, try again" + } + + if (!(Test-Path $dest -PathType Container)) { + $original_lib_src = Join-Path $prefix $Script:url_pkg_name + if (Test-Path $original_lib_src -PathType Container) { + $tries = 0 + do { + try { + Rename-Item $original_lib_src $dest -Force + if ($?) { + break + } + } + catch { + + } + + println "fetch.ps1: rename $original_lib_src to $dest failed, try after 1 seconds" + $tries += 1 + Start-Sleep -Seconds 1 + } while ($tries -lt 10) + } + else { + throw "fetch.ps1: the package name mismatch for $out" + } + } + } +} + +# parse url from $uri +$uriInfo = [array]$uri.Split('#') +$uri = $uriInfo[0] +if (!$version) { + $version = $uriInfo[1] +} + +$url = $null +if ($uri -match '^([a-z]+://|git@)') { + $url = $uri +} +elseif ($uri.StartsWith('gh:')) { + $url = "https://github.com/$($uri.substring(3))" + if (!$url.EndsWith('.git')) { $url += '.git' } +} +elseif ($uri.StartsWith('gl:')) { + $url = "https://gitlab.com/$($uri.substring(3))" + if (!$url.EndsWith('.git')) { $url += '.git' } +} +else { + $name = $uri +} + +# simple match url/ssh schema +if (!$url) { + # fetch package from manifest config + $lib_src = Join-Path $prefix $name + $mirror = if (!(Test-Path (Join-Path $PSScriptRoot '.gitee') -PathType Leaf)) { 'github' } else { 'gitee' } + $url_base = @{'github' = 'https://github.com/'; 'gitee' = 'https://gitee.com/' }[$mirror] + + $manifest_map = ConvertFrom-Json (Get-Content $manifest_file -raw) + + if (!$version) { + $version_map = $manifest_map.versions + $version = $version_map.PSObject.Properties[$name].Value + } + if ($version) { + $url_path = $manifest_map.mirrors.PSObject.Properties[$mirror].Value.PSObject.Properties[$name].Value + if ($url_path) { + $url = "$url_base/$url_path" + if (!$url.EndsWith('.git')) { $url += '.git' } + } + } +} + +if (!$url) { + throw "fetch.ps1: can't determine package url of '$name'" +} + +$url_pkg_ext = $null +$Script:url_pkg_name = $null +$match_info = [Regex]::Match($url, '(\.git)|(\.zip)|(\.tar\.(gz|bz2|xz))$') +if ($match_info.Success) { + $url_pkg_ext = $match_info.Value + $url_file_name = Split-Path $url -Leaf + $Script:url_pkg_name = $url_file_name.Substring(0, $url_file_name.Length - $url_pkg_ext.Length) + if (!$name) { + $name = $Script:url_pkg_name + } +} +else { + throw "fetch.ps1: invalid url, must be endswith .git, .zip, .tar.xx" +} + +$lib_src = Join-Path $prefix $name +$is_git_repo = $url_pkg_ext -eq '.git' +if (!$is_git_repo) { + $match_info = [Regex]::Match($url, '(\d+\.)+(-)?(\*|\d+)') + if ($match_info.Success) { + $version = $match_info.Value + } +} + +if (!$version) { + throw "fetch.ps1: can't determine package version of '$name'" +} + +Set-Variable -Name "${name}_src" -Value $lib_src -Scope global + +$sentry = Join-Path $lib_src '_1kiss' + +$is_rev_mod = $false # indicate whether rev already modfied or updated +# if sentry file missing, re-clone +if (!(Test-Path $sentry -PathType Leaf)) { + if (Test-Path $lib_src -PathType Container) { + Remove-Item $lib_src -Recurse -Force + } + + fetch_repo -url $url -name $name -dest $lib_src -ext $url_pkg_ext + + if (Test-Path $lib_src -PathType Container) { + New-Item $sentry -ItemType File 1>$null + } + else { + throw "fetch.ps1: fetch content from $url failed" + } + + $is_rev_mod = $true +} + +# re-check does valid local git repo +if (!(Test-Path "$lib_src/.git" -PathType Container)) { $is_git_repo = $false } + +# checkout revision for git repo +if (!$revision) { + $ver_pair = [array]$version.Split('-') + $use_hash = $ver_pair.Count -gt 1 + $revision = $ver_pair[$use_hash].Trim() + $version = $ver_pair[0] +} + +$branch_name = $null +if ($is_git_repo) { + $old_rev_hash = $(git -C $lib_src rev-parse HEAD) + + # The 'revision' can be branch name, tag or a commit id + $new_rev_hash = $(git -C $lib_src rev-parse --verify --quiet "$revision^{}") + if (!$new_rev_hash) { + git -C $lib_src fetch + $new_rev_hash = $(git -C $lib_src rev-parse --verify --quiet "$revision^{}") + if (!$new_rev_hash) { + throw "fetch.ps1: Could not found commit hash of $revision" + } + } + + if ($old_rev_hash -ne $new_rev_hash) { + git -C $lib_src checkout $revision 1>$null 2>$null + $cur_rev_hash = $(git -C $lib_src rev-parse HEAD) + if ($cur_rev_hash -ne $new_rev_hash) { + println "fetch.ps1: warning: cur_rev_hash($cur_rev_hash) != new_rev_hash($new_rev_hash)" + } + + $is_rev_mod = $true + } + + $branch_name = $(git -C $lib_src branch --show-current) + if ($branch_name -and $pull_branch) { + git -C $lib_src pull + $new_rev_hash = $(git -C $lib_src rev-parse HEAD) + if ($cur_rev_hash -ne $new_rev_hash) { + $cur_rev_hash = $new_rev_hash + $is_rev_mod = $true + } + } +} + +if ($is_rev_mod) { + $sentry_content = "ver: $version" + if ($is_git_repo) { + if ((Test-Path (Join-Path $lib_src '.gitmodules') -PathType Leaf)) { + git -C $lib_src submodule update --recursive --init + } + if ($branch_name) { + # tracking branch + $commits = $(git -C $lib_src rev-list --count HEAD) + $sentry_content += "`nbranch: $branch_name" + $sentry_content += "`ncommits: $commits" + $commit_id = $(git -C $lib_src rev-parse --short=7 HEAD) + $sentry_content += "`nrev: $commit_id" + println "fetch.ps1: HEAD is now at $branch_name@$commit_id" + } + else { + println "fetch.ps1: HEAD is now at $revision@$cur_rev_hash" + } + } + + [System.IO.File]::WriteAllText($sentry, $sentry_content) + + if ($is_git_repo) { git -C $lib_src add '_1kiss' } +} + +# google gclient spec +if (Test-Path (Join-Path $lib_src '.gn') -PathType Leaf) { + # the repo use google gn build system manage deps and build + Push-Location $lib_src + # angle (A GLES native implementation by google) + if (Test-Path 'scripts/bootstrap.py' -PathType Leaf) { + python scripts/bootstrap.py + } + # darwin (A WebGPU native implementation by google) + if (Test-Path 'scripts/standalone.gclient' -PathType Leaf) { + Copy-Item scripts/standalone.gclient .gclient -Force + } + gclient sync -D + Pop-Location +} diff --git a/NativeLibs/yasio/1k/fetchurl.ps1 b/NativeLibs/yasio/1k/fetchurl.ps1 deleted file mode 100644 index 4a3d857..0000000 --- a/NativeLibs/yasio/1k/fetchurl.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -param( - $name, - $cfg -) - -$mirror = if (!(Test-Path (Join-Path $PSScriptRoot '.gitee') -PathType Leaf)) {'github'} else {'gitee'} -$url_base = @{'github' = 'https://github.com/'; 'gitee' = 'https://gitee.com/' }[$mirror] - -$manifest_map = ConvertFrom-Json (Get-Content $cfg -raw) -$ver = $manifest_map.versions.PSObject.Properties[$name].Value -$url_path = $manifest_map.mirrors.PSObject.Properties[$mirror].Value.PSObject.Properties[$name].Value - -Write-Host "$url_base$url_path#$ver" -NoNewline \ No newline at end of file diff --git a/NativeLibs/yasio/1k/fsync.ps1 b/NativeLibs/yasio/1k/fsync.ps1 new file mode 100644 index 0000000..6c692e2 --- /dev/null +++ b/NativeLibs/yasio/1k/fsync.ps1 @@ -0,0 +1,84 @@ +# sync file or directory +# .\fsync.ps1 -s srcPath -d destPath -l 1 +param( + [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [string]$srcPath, + [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [string]$destPath, + [Parameter(Mandatory=$false, ValueFromPipeline=$true)] + [PSDefaultValue(Value=$null)] + $linkOnly +) + + +function ParseBoolFuzzy($value) { + $value = "$value".ToLower() + return $value.startsWith('1') -or $value.StartsWith('t') -or $value.StartsWith('y') +} + +$linkOnly = ParseBoolFuzzy($linkOnly) + +# 0: windows, 1: linux, 2: macos +$IsWin = $IsWindows -or ("$env:OS" -eq 'Windows_NT') + +# convert to native path style +if ($IsWin) { + $srcPath = $srcPath.Replace('/', '\') + $destPath = $destPath.Replace('/', '\') +} else { + $srcPath = $srcPath.Replace('\', '/') + $destPath = $destPath.Replace('\', '/') +} + +if(!$srcPath -or !(Test-Path $srcPath -PathType Any)) { + throw "fsync.ps1: The source directory $srcPath not exist" +} + +if (Test-Path $destPath -PathType Any) { # dest already exist + if ($linkOnly) { # is symlink and dest exist + $fileItem = (Get-Item $destPath) + if ($fileItem.Target -eq $srcPath) { + Write-Host "fsync.ps1: Symlink $destPath ===> $($fileItem.Target) exists" + return + } + Write-Host "fsync.ps1: Removing old link target $($fileItem.Target)" + # force delete if exist dest not symlink + if ($fileItem.PSIsContainer -and !$fileItem.Target) { + $fileItem.Delete($true) + } else { + $fileItem.Delete() + } + } +} + +$destLoc = Split-Path $destPath -Parent +if (!(Test-Path $destLoc -PathType Container)) { + New-Item $destLoc -ItemType Directory 1>$null +} + +if ($linkOnly) { + Write-Host "fsync.ps1: Symlink $srcPath to $destPath ..." + if ($IsWin -and (Test-Path $srcPath -PathType Container)) { + cmd.exe /c mklink /J $destPath $srcPath + } + else { + # ln -s $srcPath $destPath + New-Item -ItemType SymbolicLink -Path $destPath -Target $srcPath 2>$null + } +} +else { # copy + Write-Host "fsync.ps1: Copying $srcPath to $destPath ..." + if (Test-Path $srcPath -PathType Container) { + if (!(Test-Path $destPath -PathType Container)) { + Copy-Item $srcPath $destPath -Recurse -Force + } else { + Copy-Item $srcPath/* $destPath/ -Recurse -Force + } + } else { + Copy-Item $srcPath $destPath -Force + } +} + +if(!$?) { # $Error array + exit 1 +} diff --git a/NativeLibs/yasio/1k/install-pwsh.sh b/NativeLibs/yasio/1k/install-pwsh.sh new file mode 100644 index 0000000..fb86bec --- /dev/null +++ b/NativeLibs/yasio/1k/install-pwsh.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# Install powershell 7 on macOS, Ubuntu, ArchLinux to system path +# usage: ./install-pwsh [pwsh_ver] +# + +HOST_OS=$(uname) + +myRoot=$(dirname "$0") + +cacheDir=~/.1kiss +mkdir -p $cacheDir + +pwsh_ver=$1 +if [ "$pwsh_ver" = "" ] ; then + pwsh_ver='7.4.4' +fi + +pwsh_min_ver=$2 +if [ "$pwsh_min_ver" = "" ] ; then + pwsh_min_ver='7.3.0' +fi + +if [[ "$pwsh_ver" < "$pwsh_min_ver" ]] ; then + pwsh_ver=$pwsh_min_ver +fi + +function check_pwsh { + min_ver=$1 + preferred_ver=$2 + if command -v pwsh >/dev/null ; then + verx=$(pwsh --version) + very="PowerShell $min_ver" + if ([ "$preferred_ver" != "$min_ver" ] && ([[ "$verx" > "$very" ]] || [ "$verx" = "$very" ])) \ + || ([ "$preferred_ver" = "$min_ver" ] && [ "$verx" = "$very" ]) ; then + echo "1kiss: $verx installed." + exit 0 + fi + fi + echo "Installing PowerShell $preferred_ver ..." +} + +HOST_ARCH=$(uname -m) +if [ "$HOST_ARCH" = 'x86_64' ] ; then + HOST_ARCH=x64 +fi + +if [ $HOST_OS = 'Darwin' ] ; then + check_pwsh $pwsh_min_ver $preferred_ver + pwsh_pkg="powershell-$pwsh_ver-osx-$HOST_ARCH.pkg" + pwsh_pkg_out="$cacheDir/$pwsh_pkg" + if [ ! -f "$pwsh_pkg_out" ] ; then + pwsh_url="https://github.com/PowerShell/PowerShell/releases/download/v$pwsh_ver/$pwsh_pkg" + echo "Downloading $pwsh_url ..." + curl -L "$pwsh_url" -o "$pwsh_pkg_out" + fi + sudo xattr -rd com.apple.quarantine "$pwsh_pkg_out" + sudo installer -pkg "$pwsh_pkg_out" -target / +elif [ $HOST_OS = 'Linux' ] ; then + if command -v dpkg > /dev/null; then # Linux distro: deb (ubuntu) + check_pwsh $pwsh_min_ver $preferred_ver + pwsh_pkg="powershell_$pwsh_ver-1.deb_amd64.deb" + pwsh_pkg_out="$cacheDir/$pwsh_pkg" + if [ ! -f "$pwsh_pkg_out" ] ; then + curl -L "https://github.com/PowerShell/PowerShell/releases/download/v$pwsh_ver/$pwsh_pkg" -o "$pwsh_pkg_out" + fi + sudo_cmd=$(which sudo) + $sudo_cmd dpkg -i "$pwsh_pkg_out" + $sudo_cmd apt-get install -f + elif command -v pacman > /dev/null; then # Linux distro: Arch + # refer: https://ephos.github.io/posts/2018-9-17-Pwsh-ArchLinux + # available pwsh version, refer to: https://aur.archlinux.org/packages/powershell-bin + check_pwsh $pwsh_min_ver + git clone https://aur.archlinux.org/powershell-bin.git $cacheDir/powershell-bin + cd $cacheDir/powershell-bin + makepkg -si --needed --noconfirm + cd - + fi +else + echo "Unsupported HOST OS: $HOST_OS" + exit 1 +fi + +if command -v pwsh >/dev/null ; then + installed_pwsh_ver=$(pwsh --version) + echo "Install PowerShell $installed_pwsh_ver succeed." +else + echo "Install PowerShell fail, try again" + if [ -f "$pwsh_pkg_out" ] ; then + rm -f "$pwsh_pkg_out" + fi +fi diff --git a/NativeLibs/yasio/1k/ios.cmake b/NativeLibs/yasio/1k/ios.cmake new file mode 100644 index 0000000..f845863 --- /dev/null +++ b/NativeLibs/yasio/1k/ios.cmake @@ -0,0 +1,158 @@ +# +# The minimal ios toolchain file: https://github.com/simdsoft/1kiss/blob/dev/1k/ios.cmake +# version: 4.1.3 +# +# The supported params: +# PLAT: iOS, tvOS, default: iOS +# ARCHS: arm64, x86_64, default: arm64 +# DEPLOYMENT_TARGET: default: iOS=11.0/12.0, tvOS=15.0, watchOS=8.0 +# SIMULATOR: TRUE, FALSE, UNDEFINED(auto determine by archs) +# ENABLE_BITCODE: FALSE(default) +# +# !!!Note: iOS simulator, there is no xcode General tab, and we must set +# CMAKE_OSX_SYSROOT properly for simulator, otherwise will lead cmake based +# project detect C/C++ header from device sysroot which is not present in +# simulator sysroot, then cause compiling errors +# +# + +# PLAT +if(NOT DEFINED PLAT) + set(PLAT iOS CACHE STRING "" FORCE) +endif() + +# ARCHS +if(NOT DEFINED ARCHS) + set(ARCHS "arm64" CACHE STRING "" FORCE) +endif() + +# DEPLOYMENT_TARGET +if(NOT DEFINED DEPLOYMENT_TARGET) + if (PLAT STREQUAL "iOS") + if("${ARCHS}" MATCHES ".*armv7.*") + set(DEPLOYMENT_TARGET "10.0" CACHE STRING "" FORCE) + else() + if (XCODE_VERSION LESS "14.3.0") + set(DEPLOYMENT_TARGET "11.0" CACHE STRING "" FORCE) + else() # xcode 14.3+ require 12.0 for c++ std::get + set(DEPLOYMENT_TARGET "12.0" CACHE STRING "" FORCE) + endif() + endif() + elseif (PLAT STREQUAL "tvOS") + set(DEPLOYMENT_TARGET "15.0" CACHE STRING "" FORCE) + elseif (PLAT STREQUAL "watchOS") + set(DEPLOYMENT_TARGET "8.0" CACHE STRING "" FORCE) + else() + message(FATAL_ERROR "PLAT=${PLAT} unsupported!") + endif() +endif() + +# SIMULATOR, regards x86_64 as simulator if SIMULATOR not defined +if((NOT DEFINED SIMULATOR) AND ("${ARCHS}" STREQUAL "x86_64")) + set(SIMULATOR TRUE CACHE BOOL "" FORCE) +endif() + +# ENABLE_BITCODE, default OFF, xcode14: Building with bitcode is deprecated. Please update your project and/or target settings to disable bitcode +if(NOT DEFINED ENABLE_BITCODE) + set(ENABLE_BITCODE FALSE CACHE BOOL "" FORCE) +endif() + +# apply params +set(CMAKE_SYSTEM_NAME ${PLAT} CACHE STRING "") +set(CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE STRING "") +set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET} CACHE STRING "") + +# The best solution for fix try_compile failed with code sign currently +# since cmake-3.18.2, not required +# everyting for cmake toolchain config before project(xxx) is better +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + ${CMAKE_TRY_COMPILE_PLATFORM_VARIABLES} + "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED" + "CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED") +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED NO) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO) + +# set(CMAKE_BUILD_WITH_INSTALL_RPATH YES) +# set(CMAKE_INSTALL_RPATH "@executable_path/Frameworks") +# set(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks" ${CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS}) + +# Fix compile failed with armv7 deployment target >= 11.0, xcode clang will report follow error +# clang: error: invalid iOS deployment version '--target=armv7-apple-ios13.6', +# iOS 10 is the maximum deployment target for 32-bit targets +# If not defined CMAKE_OSX_DEPLOYMENT_TARGET, cmake will choose latest deployment target +if(NOT DEFINED CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET) + set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET} CACHE STRING "") +endif() + +if (NOT SIMULATOR) + if(PLAT STREQUAL "iOS") + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") + elseif(PLAT STREQUAL "tvOS") + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-appletvos") + elseif(PLAT STREQUAL "watchOS") + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchos") + endif() +else() + if (PLAT STREQUAL "iOS") + set(_SDK_NAME "iphonesimulator") + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") + elseif(PLAT STREQUAL "tvOS") + set(_SDK_NAME "appletvsimulator") + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-tvsimulator") + + elseif(PLAT STREQUAL "watchOS") + set(_SDK_NAME "watchsimulator") + set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchsimulator") + else() + message(FATAL_ERROR "PLAT=${PLAT} unsupported!") + endif() + + find_program(XCODEBUILD_PROG xcodebuild) + if(NOT XCODEBUILD_PROG) + message(FATAL_ERROR "xcodebuild not found. Please install either the standalone commandline tools or Xcode.") + endif() + execute_process(COMMAND ${XCODEBUILD_PROG} -version -sdk ${_SDK_NAME} SDKVersion + OUTPUT_VARIABLE _SDK_VER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_OSX_SYSROOT "${_SDK_NAME}${_SDK_VER}" CACHE STRING "") +endif() + +# Since xcode14, the bitcode was marked deprecated, so we disable by default +if(ENABLE_BITCODE) + set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE "bitcode") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES") +else() + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") +endif() + +# Set find path mode properly for cross-compiling +# refer to: https://discourse.cmake.org/t/find-package-stops-working-when-cmake-system-name-ios/4609/6 +# BUT: CMAKE_FIND_ROOT_PATH is preferred for additional search directories when cross-compiling +# set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH CACHE STRING "") +# set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH CACHE STRING "") +# set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH CACHE STRING "") + +# by default, we want find host program only when cross-compiling +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER CACHE STRING "") + +# Sets CMAKE_SYSTEM_PROCESSOR properly +if(ARCHS MATCHES "((arm64|arm64e|x86_64)(^|;|, )?)+") + set(CMAKE_C_SIZEOF_DATA_PTR 8) + set(CMAKE_CXX_SIZEOF_DATA_PTR 8) + if(ARCHS MATCHES "((arm64|arm64e)(^|;|, )?)+") + set(CMAKE_SYSTEM_PROCESSOR "arm64") + else() + set(CMAKE_SYSTEM_PROCESSOR "x86_64") + endif() +endif() + +# This little function lets you set any XCode specific property, refer to: ios.toolchain.cmake +function(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) + if (ARGC LESS 4 OR ARGV3 STREQUAL "All") + set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) + else() + set(XCODE_RELVERSION ${ARGV3}) + set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION}] "${XCODE_VALUE}") + endif() +endfunction() diff --git a/NativeLibs/yasio/1k/manifest.ps1 b/NativeLibs/yasio/1k/manifest.ps1 new file mode 100644 index 0000000..8d5a912 --- /dev/null +++ b/NativeLibs/yasio/1k/manifest.ps1 @@ -0,0 +1,7 @@ +# Default manifest, refer in 1k/1kiss.ps1 + +if ($IsWin) { + $manifest['nasm'] = '2.16.03+' +} else { + $manifest['nasm'] = '2.15.05+' +} diff --git a/NativeLibs/yasio/1k/platform.cmake b/NativeLibs/yasio/1k/platforms.cmake similarity index 66% rename from NativeLibs/yasio/1k/platform.cmake rename to NativeLibs/yasio/1k/platforms.cmake index b51f824..b23239a 100644 --- a/NativeLibs/yasio/1k/platform.cmake +++ b/NativeLibs/yasio/1k/platforms.cmake @@ -13,10 +13,11 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(WINDOWS TRUE) - if("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32") + string(TOLOWER "${CMAKE_GENERATOR_PLATFORM}" _gp_lcase) + if("${_gp_lcase}" MATCHES "win32") set(WIN32 TRUE) set(ARCH_ALIAS "x86") - elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "arm64") + elseif("${_gp_lcase}" STREQUAL "arm64") set(WIN64 TRUE) set(ARCH_ALIAS "arm64") else() @@ -35,9 +36,13 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(LINUX TRUE) set(PLATFORM_NAME linux) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") - set(WASM TRUE) - set(EMSCRIPTEN TRUE) - set(PLATFORM_NAME wasm) + set(WASM TRUE) + set(EMSCRIPTEN TRUE) + if ("${CMAKE_LIBRARY_ARCHITECTURE}" MATCHES "64") + set(PLATFORM_NAME wasm64) + else() + set(PLATFORM_NAME wasm) + endif() elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(APPLE TRUE) set(MACOSX TRUE) @@ -51,9 +56,11 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "tvOS") set(IOS TRUE) set(TVOS TRUE) set(PLATFORM_NAME tvos) +elseif(${CMAKE_SYSTEM_NAME} MATCHES "OHOS") + set(OHOS TRUE) + set(PLATFORM_NAME ohos) else() - message(AUTHOR_WARNING "Unsupported platform to use prebuilt libs") - return() + message(AUTHOR_WARNING "Unhandled platform: ${CMAKE_SYSTEM_NAME}") endif() if (NOT DEFINED WIN32) @@ -69,6 +76,21 @@ elseif(CMAKE_GENERATOR MATCHES Visual) set(VS TRUE) endif() +# The global rpath settings +if(LINUX OR APPLE) + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE CACHE BOOL "" FORCE) + if(LINUX) + set(CMAKE_INSTALL_RPATH ".:\$ORIGIN:\$ORIGIN/../lib:${CMAKE_BINARY_DIR}/lib") + elseif(APPLE) + set(CMAKE_SKIP_BUILD_RPATH TRUE CACHE BOOL "" FORCE) + if(IOS) + set(CMAKE_INSTALL_RPATH "/usr/lib/swift" "@executable_path/Frameworks") + else() + set(CMAKE_INSTALL_RPATH "@executable_path/../Frameworks") + endif() + endif() +endif() + function(_1k_deprecated_32bit project_name release_ver) if(("${ARCH_ALIAS}" STREQUAL "x86") OR ("${ARCH_ALIAS}" MATCHES "armeabi-v7a")) message(WARNING "Building 32-bit[${ARCH_ALIAS}] ${project_name} is deprecated, and will be removed in next release ${release_ver}") diff --git a/NativeLibs/yasio/1k/resolv-uri.ps1 b/NativeLibs/yasio/1k/resolv-uri.ps1 new file mode 100644 index 0000000..b873d02 --- /dev/null +++ b/NativeLibs/yasio/1k/resolv-uri.ps1 @@ -0,0 +1,16 @@ +# fetch repo url by name +param( + $name, + $manifest_file +) + +if(Test-Path $manifest_file -PathType Leaf) { + $mirror = if (!(Test-Path (Join-Path $PSScriptRoot '.gitee') -PathType Leaf)) {'github'} else {'gitee'} + $url_base = @{'github' = 'https://github.com/'; 'gitee' = 'https://gitee.com/' }[$mirror] + + $manifest_map = ConvertFrom-Json (Get-Content $manifest_file -raw) + $ver = $manifest_map.versions.PSObject.Properties[$name].Value + $url_path = $manifest_map.mirrors.PSObject.Properties[$mirror].Value.PSObject.Properties[$name].Value + + Write-Host "$url_base$url_path#$ver" -NoNewline +} diff --git a/NativeLibs/yasio/1k/setup-msvc.ps1 b/NativeLibs/yasio/1k/setup-msvc.ps1 new file mode 100644 index 0000000..eb06c16 --- /dev/null +++ b/NativeLibs/yasio/1k/setup-msvc.ps1 @@ -0,0 +1,23 @@ +param( + $ver = '14.39', + $arch = 'x64' +) +$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" +$vs_installs = ConvertFrom-Json "$(&$vswhere -version '17.0' -format 'json')" +$vs_installs +$vs_installer = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\setup.exe" +$vs_path = $vs_installs[0].installationPath + +$vs_arch = @{x64 = 'x86.x64'; x86 = 'x86.x64'; arm64 = 'ARM64'; arm = 'ARM' }[$arch] +$msvc_comp_id = "Microsoft.VisualStudio.Component.VC.$ver.17.9.$vs_arch" # refer to: https://learn.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2022 +echo "Installing $msvc_comp_id ..." +&$vs_installer modify --quiet --installPath $vs_path --add $msvc_comp_id | Out-Host + +if ($?) { + echo 'setup msvc success.' +} +else { + echo 'setup msvc fail' +} + +exit $LASTEXITCODE diff --git a/NativeLibs/yasio/3rdparty/kcp/ikcp.c b/NativeLibs/yasio/3rdparty/kcp/ikcp.c index 6e14bc9..d030dbd 100644 --- a/NativeLibs/yasio/3rdparty/kcp/ikcp.c +++ b/NativeLibs/yasio/3rdparty/kcp/ikcp.c @@ -17,7 +17,7 @@ #include #include - +#define IKCP_FASTACK_CONSERVE //===================================================================== // KCP BASIC diff --git a/NativeLibs/yasio/CMakeLists.txt b/NativeLibs/yasio/CMakeLists.txt index 4e75a3a..e4a813e 100644 --- a/NativeLibs/yasio/CMakeLists.txt +++ b/NativeLibs/yasio/CMakeLists.txt @@ -21,7 +21,7 @@ project(${yasio_target_name}) set(_1kfetch_cache_dir "${CMAKE_CURRENT_LIST_DIR}/cache" CACHE STRING "" FORCE) set(_1kfetch_manifest "${CMAKE_CURRENT_LIST_DIR}/manifest.json" CACHE STRING "" FORCE) -include(1k/platform.cmake) +include(1k/platforms.cmake) include(1k/fetch.cmake) include(CMakeDependentOption) @@ -38,7 +38,11 @@ option(YASIO_ENABLE_LUA "Build yasio with lua support" OFF) option(YASIO_ENABLE_AXLUA "Build yasio with axmol-lua support" OFF) option(YASIO_ENABLE_NI "Build yasio with native interface for interop" OFF) option(YASIO_ENABLE_KCP "Enable kcp support" OFF) -option(YASIO_NO_DEPS "Build yasio without deps" OFF) +if (NOT OHOS) + option(YASIO_NO_DEPS "Build yasio without deps" OFF) +else() + set(YASIO_NO_DEPS ON) # disable 3rd deps for ohos +endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) @@ -122,7 +126,7 @@ endif() if (NOT DEFINED YASIO_SSL_BACKEND) if (CMAKE_SYSTEM_NAME MATCHES "Emscripten") set(YASIO_SSL_BACKEND 0) - elseif(CMAKE_SYSTEM_NAME STREQUAL "watchOS") + elseif(CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR (MSVC_VERSION AND MSVC_VERSION LESS 1900)) set(YASIO_SSL_BACKEND 2) # mbedtls else() set(YASIO_SSL_BACKEND 1) # openssl @@ -142,11 +146,14 @@ endif() set (YASIO_USE_PREBUILT_LUA FALSE) -if (ANDROID) - if (NOT THIRDPART_LIB) - set(THIRDPART_LIB "-llog") - else() - set(THIRDPART_LIB "${THIRDPART_LIB} -llog") +if(WIN32) + set(YASIO_EXTERN_LIBS "ws2_32") +else() + set(YASIO_EXTERN_LIBS "m") + if (ANDROID) + list(APPEND YASIO_EXTERN_LIBS "log") + elseif(OHOS) + list(APPEND YASIO_EXTERN_LIBS "hilog_ndk.z") endif() endif() @@ -176,20 +183,25 @@ elseif(YASIO_SSL_BACKEND EQUAL 2) # mbedtls message(STATUS "Build with mbedtls support") set(ENABLE_PROGRAMS OFF CACHE BOOL "Build mbedtls programs" FORCE) set(ENABLE_TESTING OFF CACHE BOOL "Build mbed TLS tests." FORCE) - set(MBEDTLS_VER "3.4.1") - if(MSVC_VERSION LESS 1900) - message(AUTHOR_WARNING "Using mbedtls 2.28.3 for vs2013 happy") - set(MBEDTLS_VER "2.28.3") + if(MSVC_VERSION AND (MSVC_VERSION LESS 1900)) + set(MBEDTLS_VER "2.28.7") + message(AUTHOR_WARNING "Using mbedtls ${MBEDTLS_VER} for vs2013 happy") + else() + set(MBEDTLS_VER "3.6.0") + endif() + if (PWSH_COMMAND) + _1kadd_pkg("gh:Mbed-TLS/mbedtls#v${MBEDTLS_VER}" OPTIONS "MBEDTLS_FATAL_WARNINGS OFF") + else() + include(1k/CPM.cmake) + CPMAddPackage( + NAME mbedtls + VERSION ${MBEDTLS_VER} + GITHUB_REPOSITORY "Mbed-TLS/mbedtls" + GIT_TAG "v${MBEDTLS_VER}" + OPTIONS + "MBEDTLS_FATAL_WARNINGS OFF" + ) endif() - include(1k/CPM.cmake) - CPMAddPackage( - NAME mbedtls - VERSION ${MBEDTLS_VER} - GITHUB_REPOSITORY "Mbed-TLS/mbedtls" - GIT_TAG "v${MBEDTLS_VER}" - OPTIONS - "MBEDTLS_FATAL_WARNINGS OFF" - ) yasio_config_target_outdir(mbedtls) yasio_config_target_outdir(mbedcrypto) yasio_config_target_outdir(mbedx509) @@ -201,44 +213,33 @@ if (YASIO_USE_CARES) endif() ### The yasio core library project -file(GLOB YASIO_CORE yasio/*.hpp;yasio/*.cpp;yasio/compiler/*.hpp;yasio/impl/*.hpp;) +file(GLOB YASIO_SOURCES yasio/*.hpp;yasio/*.cpp;yasio/compiler/*.hpp;yasio/impl/*.hpp) if (YASIO_ENABLE_KCP) if (NOT kcp_SOURCE_DIR) - set(YASIO_CORE ${YASIO_CORE} - ${PROJECT_SOURCE_DIR}/3rdparty/kcp/ikcp.c - ) + list(APPEND YASIO_SOURCES ${PROJECT_SOURCE_DIR}/3rdparty/kcp/ikcp.c) list(APPEND YASIO_INC_DIRS "${PROJECT_SOURCE_DIR}/3rdparty/kcp") else() - list(APPEND THIRDPART_LIB "kcp") + list(APPEND YASIO_EXTERN_LIBS "kcp") endif() endif() if (YASIO_ENABLE_NI) - set(YASIO_CORE ${YASIO_CORE} - yasio/bindings/yasio_ni.cpp - ) + list(APPEND YASIO_SOURCES yasio/bindings/yasio_ni.cpp) endif() if (YASIO_ENABLE_LUA) - set(YASIO_CORE ${YASIO_CORE} - yasio/bindings/lyasio.cpp - ) - - if(YASIO_ENABLE_AXLUA) - set(YASIO_CORE ${YASIO_CORE} - yasio/bindings/yasio_axlua.cpp - ) - endif() + list(APPEND YASIO_SOURCES yasio/bindings/lyasio.cpp) + if(YASIO_ENABLE_AXLUA) + list(APPEND YASIO_SOURCES yasio/bindings/yasio_axlua.cpp) + endif() endif() if(ANDROID AND CARES_INCLUDE_DIR) - set(YASIO_CORE ${YASIO_CORE} - yasio/platform/yasio_jni.cpp - ) + list(APPEND YASIO_SOURCES yasio/platform/yasio_jni.cpp) endif() if(WIN32 AND YASIO_ENABLE_HPERF_IO) - set(THIRDPART_SRC 3rdparty/wepoll/wepoll.c) + list(APPEND YASIO_SOURCES 3rdparty/wepoll/wepoll.c) endif() if(YASIO_ENABLE_KCP) @@ -262,55 +263,30 @@ macro(source_group_by_dir proj_dir source_files) endif(MSVC OR APPLE) endmacro(source_group_by_dir) -source_group_by_dir(${CMAKE_CURRENT_SOURCE_DIR} YASIO_CORE) +source_group_by_dir(${CMAKE_CURRENT_SOURCE_DIR} YASIO_SOURCES) # --- add yasio core lib project if (APPLE) if (IOS) # set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD)") - add_library(${yasio_target_name} STATIC - ${YASIO_CORE} - ${THIRDPART_SRC} - ) + add_library(${yasio_target_name} STATIC ${YASIO_SOURCES}) else () # set(CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_64_BIT)") if (BUILD_SHARED_LIBS) - add_library(${yasio_target_name} MODULE - ${YASIO_CORE} - ${THIRDPART_SRC} - ) + add_library(${yasio_target_name} MODULE ${YASIO_SOURCES}) set_target_properties (${yasio_target_name} PROPERTIES BUNDLE TRUE ) else() - add_library(${yasio_target_name} STATIC - ${YASIO_CORE} - ${THIRDPART_SRC} - ) + add_library(${yasio_target_name} STATIC ${YASIO_SOURCES}) endif() endif () elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Switch") - add_library(${yasio_target_name} STATIC - ${YASIO_CORE} - ${THIRDPART_SRC} - ) + add_library(${yasio_target_name} STATIC ${YASIO_SOURCES}) target_compile_options(${yasio_target_name} PRIVATE -m64 -mcpu=cortex-a57+fp+simd+crypto+crc -fno-common -fno-short-enums -ffunction-sections -fdata-sections -fPIC -fms-extensions) else ( ) # linux/win32/android - add_library(${yasio_target_name} - ${YASIO_CORE} - ${THIRDPART_SRC} - ) + add_library(${yasio_target_name} ${YASIO_SOURCES}) endif ( ) -if ( WIN32 ) - target_link_libraries(${yasio_target_name} - ws2_32 - ${THIRDPART_LIB} - ) -else ( ) - target_link_libraries(${yasio_target_name} - m - ${THIRDPART_LIB} - ) -endif ( ) +target_link_libraries(${yasio_target_name} ${YASIO_EXTERN_LIBS}) message(STATUS "YASIO_INC_DIRS=${YASIO_INC_DIRS}") target_include_directories(${yasio_target_name} PUBLIC ${YASIO_INC_DIRS}) @@ -402,13 +378,6 @@ if(CARES_INCLUDE_DIR AND YASIO_USE_CARES) target_link_libraries(${yasio_target_name} c-ares) endif() -if (BUILD_SHARED_LIBS AND ANDROID) - message(STATUS "CMAKE_STRIP=${CMAKE_STRIP}") - add_custom_command(TARGET ${yasio_target_name} POST_BUILD - COMMAND mkdir -p ${PROJECT_BINARY_DIR}/stripped && ${CMAKE_STRIP} ${PROJECT_BINARY_DIR}/libyasio.so -o ${PROJECT_BINARY_DIR}/stripped/libyasio.so - ) -endif() - # lua if(YASIO_ENABLE_LUA) find_package(Lua "5.1.0") @@ -428,7 +397,7 @@ if(YASIO_ENABLE_LUA) endif() endif() -# link libraries for yasio_core when BUILD_SHARED_LIBS=TRUE +# link libraries for yasio_SOURCES when BUILD_SHARED_LIBS=TRUE yasio_link_ssl_libraries(${yasio_target_name}) if(BUILD_SHARED_LIBS) if(NOT WIN32 AND (NOT ANDROID)) diff --git a/NativeLibs/yasio/build_win.bat b/NativeLibs/yasio/build_win.bat index 34f57e5..0f7d3b6 100644 --- a/NativeLibs/yasio/build_win.bat +++ b/NativeLibs/yasio/build_win.bat @@ -1,12 +1,14 @@ +set build_cfg=%1 +if not defined build_cfg set build_cfg=MinSizeRel set cmake_options=-DBUILD_SHARED_LIBS=ON -DYASIO_NO_DEPS=ON -DYASIO_ENABLE_NI=ON -DYASIO_ENABLE_KCP=ON -DYASIO_BUILD_TESTS=OFF cmake -B build_x64 -A x64 %cmake_options% -cmake --build build_x64 --config MinSizeRel +cmake --build build_x64 --config %build_cfg% md plugin_win\Plugins\x86_64 -copy /y build_x64\MinSizeRel\yasio.dll plugin_win\Plugins\x86_64\ +copy /y build_x64\%build_cfg%\yasio.dll plugin_win\Plugins\x86_64\ cmake -B build_x86 -A Win32 %cmake_options% -cmake --build build_x86 --config MinSizeRel +cmake --build build_x86 --config %build_cfg% md plugin_win\Plugins\x86 -copy /y build_x86\MinSizeRel\yasio.dll plugin_win\Plugins\x86\ +copy /y build_x86\%build_cfg%\yasio.dll plugin_win\Plugins\x86\ if "%GITHUB_ACTIONS%"=="" copy /y plugin_win\Plugins\x86_64\yasio.dll ..\..\Assets\Plugins\x86_64\ diff --git a/NativeLibs/yasio/manifest.json b/NativeLibs/yasio/manifest.json index 9ed3a2a..433f64a 100644 --- a/NativeLibs/yasio/manifest.json +++ b/NativeLibs/yasio/manifest.json @@ -1,6 +1,6 @@ { "versions": { - "1kdist": "72" + "1kdist": "v90" }, "mirrors": { "github": { diff --git a/NativeLibs/yasio/yasio/config.hpp b/NativeLibs/yasio/yasio/config.hpp index 77c35c1..c41511f 100644 --- a/NativeLibs/yasio/yasio/config.hpp +++ b/NativeLibs/yasio/yasio/config.hpp @@ -205,7 +205,7 @@ SOFTWARE. /* ** The yasio version macros */ -#define YASIO_VERSION_NUM 0x040203 +#define YASIO_VERSION_NUM 0x040301 /* ** The macros used by io_service. diff --git a/NativeLibs/yasio/yasio/impl/eventfd_select_interrupter.hpp b/NativeLibs/yasio/yasio/impl/eventfd_select_interrupter.hpp index eb5fe28..55d3295 100644 --- a/NativeLibs/yasio/yasio/impl/eventfd_select_interrupter.hpp +++ b/NativeLibs/yasio/yasio/impl/eventfd_select_interrupter.hpp @@ -20,11 +20,11 @@ #include #include #include -#if defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) -# include -#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +#if defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) && !defined(__UCLIBC__) +# include // for syscall without API: eventfd +#else # include -#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 +#endif #include @@ -105,7 +105,7 @@ class eventfd_select_interrupter { // Open the descriptors. Throws on error. inline void open_descriptors() { -#if defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) +#if defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) && !defined(__UCLIBC__) write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); if (read_descriptor_ != -1) { diff --git a/NativeLibs/yasio/yasio/io_service.cpp b/NativeLibs/yasio/yasio/io_service.cpp index fa39f07..9578e1f 100644 --- a/NativeLibs/yasio/yasio/io_service.cpp +++ b/NativeLibs/yasio/yasio/io_service.cpp @@ -376,7 +376,10 @@ int io_transport::write(io_send_buffer&& buffer, completion_cb_t&& handler) get_service().wakeup(); return n; } -int io_transport::do_read(int revent, int& error, highp_time_t&) { return this->call_read(buffer_ + offset_, sizeof(buffer_) - offset_, revent, error); } +int io_transport::do_read(int revent, int& error, highp_time_t&) +{ + return this->call_read(buffer_.data() + offset_, static_cast(buffer_.size() - offset_), revent, error); +} bool io_transport::do_write(highp_time_t& wait_duration) { bool ret = false; @@ -728,7 +731,14 @@ int io_transport_kcp::do_read(int revent, int& error, highp_time_t& wait_duratio this->handle_input(rawbuf_.data(), n, error, wait_duration); if (!error) { // !important, should always try to call ikcp_recv when no error occured. - n = ::ikcp_recv(kcp_, buffer_ + offset_, sizeof(buffer_) - offset_); + auto kdata_size = ::ikcp_peeksize(kcp_); + if (kdata_size > 0) + { + auto need_size = static_cast(kdata_size + offset_); + if (buffer_.size() < need_size) + buffer_.resize(kdata_size + offset_); + n = ::ikcp_recv(kcp_, buffer_.data() + offset_, kdata_size); + } if (n > 0) // If got data from kcp, don't wait wait_duration = 0; else if (n < 0) @@ -821,6 +831,15 @@ void io_service::handle_stop() return; } this->worker_.join(); + if (this->state_ != state::AT_EXITING) + { + // after join if state not AT_EXITING, means worker thread was terminated externally + // i.g .net managed exception occurred when invoke c# delegate and we should clear pending + // events to prevent dispatch to io event handler again + YASIO_KLOGW("[core] the worker thread terminated unexpectedly"); + handle_worker_exit(); + this->events_.clear(); + } } if (this->state_ != state::AT_EXITING) @@ -1005,6 +1024,10 @@ void io_service::run() } while (!this->stop_flag_ || !this->transports_.empty()); + handle_worker_exit(); +} +void io_service::handle_worker_exit() +{ #if defined(YASIO_USE_CARES) destroy_ares_channel(); #endif @@ -1012,7 +1035,6 @@ void io_service::run() cleanup_ssl_context(YSSL_CLIENT); cleanup_ssl_context(YSSL_SERVER); #endif - this->state_ = io_service::state::AT_EXITING; } void io_service::process_transports() @@ -1767,7 +1789,7 @@ bool io_service::do_read(transport_handle_t transport) const int bytes_to_strip = transport->ctx_->uparams_.initial_bytes_to_strip; if (transport->expected_size_ == -1) { // decode length - int length = transport->ctx_->decode_len_(transport->buffer_, transport->offset_ + n); + int length = transport->ctx_->decode_len_(transport->buffer_.data(), transport->offset_ + n); if (length > 0) { if (length < bytes_to_strip) @@ -1793,7 +1815,7 @@ bool io_service::do_read(transport_handle_t transport) } else if (n > 0) { // forward packet, don't perform unpack, it's useful for implement streaming based protocol, like http, websocket and ... - this->forward_packet(transport->cindex(), io_packet_view{transport->buffer_, n}, transport); + this->forward_packet(transport->cindex(), io_packet_view{transport->buffer_.data(), n}, transport); } } else @@ -1811,7 +1833,7 @@ void io_service::unpack(transport_handle_t transport, int bytes_want /*want cons auto& offset = transport->offset_; auto bytes_available = bytes_transferred + offset; auto& pkt = transport->expected_packet_; - pkt.insert(pkt.end(), transport->buffer_ + bytes_to_strip, transport->buffer_ + (std::min)(bytes_want, bytes_available)); + pkt.insert(pkt.end(), transport->buffer_.data() + bytes_to_strip, transport->buffer_.data() + (std::min)(bytes_want, bytes_available)); // set 'offset' to bytes of remain buffer offset = bytes_available - bytes_want; @@ -1819,7 +1841,7 @@ void io_service::unpack(transport_handle_t transport, int bytes_want /*want cons { /* pdu received properly */ if (offset > 0) { /* move remain data to head of buffer and hold 'offset'. */ - ::memmove(transport->buffer_, transport->buffer_ + bytes_want, offset); + ::memmove(transport->buffer_.data(), transport->buffer_.data() + bytes_want, offset); this->wait_duration_ = 0; } // move properly pdu to ready queue, the other thread who care about will retrieve it. diff --git a/NativeLibs/yasio/yasio/io_service.hpp b/NativeLibs/yasio/yasio/io_service.hpp index eba21ae..f16b0f7 100644 --- a/NativeLibs/yasio/yasio/io_service.hpp +++ b/NativeLibs/yasio/yasio/io_service.hpp @@ -778,7 +778,7 @@ class io_transport : public io_base { bool is_valid() const { return ctx_ != nullptr; } - char buffer_[yasio__max_rcvbuf]; // recv buffer, 64K + yasio::sbyte_buffer buffer_{static_cast(yasio__max_rcvbuf)}; int offset_ = 0; // recv buffer offset int expected_size_ = -1; @@ -1199,6 +1199,7 @@ class YASIO_API io_service // lgtm [cpp/class-many-fields] YASIO__DECL bool cleanup_channel(io_channel* channel, bool clear_mask = true); YASIO__DECL bool cleanup_io(io_base* obj, bool clear_mask = true); + YASIO__DECL void handle_worker_exit(); YASIO__DECL void handle_close(transport_handle_t); diff --git a/NativeLibs/yasio/yasio/logging.hpp b/NativeLibs/yasio/yasio/logging.hpp index 12b79b9..11f2804 100644 --- a/NativeLibs/yasio/yasio/logging.hpp +++ b/NativeLibs/yasio/yasio/logging.hpp @@ -40,6 +40,9 @@ inline void yasio__print(std::string&& message) { ::write(::fileno(stdout), mess # include # include # define YASIO_LOG_TAG(tag, format, ...) __android_log_print(ANDROID_LOG_INFO, "yasio", (tag format), ##__VA_ARGS__) +#elif defined(__OHOS__) +# include +# define YASIO_LOG_TAG(tag, format, ...) OH_LOG_INFO(LOG_APP, (tag format "\n"), ##__VA_ARGS__) #else # define YASIO_LOG_TAG(tag, format, ...) printf((tag format "\n"), ##__VA_ARGS__) #endif diff --git a/NativeLibs/yasio/yasio/xxsocket.cpp b/NativeLibs/yasio/yasio/xxsocket.cpp index 9477578..6c1e06f 100644 --- a/NativeLibs/yasio/yasio/xxsocket.cpp +++ b/NativeLibs/yasio/yasio/xxsocket.cpp @@ -209,7 +209,7 @@ int xxsocket::pserve(const endpoint& ep) if (!this->reopen(ep.af())) return -1; - set_optval(SOL_SOCKET, SO_REUSEADDR, 1); + this->reuse_address(true); int n = this->bind(ep); if (n != 0)