mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-21 03:40:40 +08:00
Compare commits
4
Commits
ba2afc982b
...
73e3bf709d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73e3bf709d | ||
|
|
6d90c28513 | ||
|
|
c944db6519 | ||
|
|
fc921c9ca1 |
@@ -87,7 +87,7 @@ if [[ ! -x "$BUILD_BINARY" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for command_name in readelf ldd dpkg-deb; do
|
||||
for command_name in readelf ldd dpkg-deb strip; do
|
||||
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||
echo "Required packaging command is missing: $command_name" >&2
|
||||
exit 1
|
||||
@@ -128,6 +128,9 @@ if [[ -n "$SLINT_NEEDED" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
install -m 0755 "$SLINT_LIBRARY" "$PRIVATE_DIR/$SLINT_NEEDED"
|
||||
# Keep dynamic exports/relocations while removing unneeded symbols from the
|
||||
# staged copy. Never strip Xmake's cached runtime in place.
|
||||
strip --strip-unneeded "$PRIVATE_DIR/$SLINT_NEEDED"
|
||||
fi
|
||||
|
||||
cat > "$BIN_DIR/$PKG_NAME" <<'EOF'
|
||||
|
||||
@@ -11,7 +11,7 @@ EXPECTED_ARCH="$2"
|
||||
APP_EXECUTABLE="$APP_BUNDLE/Contents/MacOS/CrossDesk"
|
||||
FRAMEWORKS_DIR="$APP_BUNDLE/Contents/Frameworks"
|
||||
|
||||
for command_name in codesign install_name_tool lipo otool; do
|
||||
for command_name in codesign install_name_tool lipo otool strip; do
|
||||
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||
echo "Required bundling command is missing: $command_name" >&2
|
||||
exit 1
|
||||
@@ -123,7 +123,11 @@ if ! otool -l "$APP_EXECUTABLE" | awk '
|
||||
install_name_tool -add_rpath "@executable_path/../Frameworks" "$APP_EXECUTABLE"
|
||||
fi
|
||||
|
||||
# install_name_tool invalidates the linker's ad-hoc signature. Sign nested code
|
||||
# Strip only the staged runtime, retaining exported symbols for Slint's C ABI.
|
||||
# The package cache remains intact for debugging and other consumers.
|
||||
strip -S -x "$FRAMEWORKS_DIR/$SLINT_NAME"
|
||||
|
||||
# install_name_tool and strip invalidate the linker's ad-hoc signature. Sign nested code
|
||||
# first, then seal the complete app bundle so local packages remain launchable.
|
||||
codesign --force --sign - "$FRAMEWORKS_DIR/$SLINT_NAME"
|
||||
codesign --force --deep --sign - "$APP_BUNDLE"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
-- Run from the repository root: xmake lua apps/desktop/tests/build_define_scope_test.lua windows true
|
||||
-- Inspect the real Xmake target graph without configuring/downloading packages.
|
||||
function main(plat, portable)
|
||||
import("core.project.config")
|
||||
import("core.project.project")
|
||||
config.load()
|
||||
config.set("plat", plat or "windows", {force = true})
|
||||
config.set("arch", plat == "macosx" and "arm64" or "x64", {force = true})
|
||||
config.set("CROSSDESK_PORTABLE", portable == "true", {force = true})
|
||||
config.set("CROSSDESK_VERSION", "v1.5.3-2-20260913", {force = true})
|
||||
local targets = project.targets()
|
||||
local function flatten(values)
|
||||
local result = {}
|
||||
local function append(value)
|
||||
if type(value) == "table" then
|
||||
for _, entry in ipairs(value) do append(entry) end
|
||||
elseif value ~= nil then
|
||||
table.insert(result, value)
|
||||
end
|
||||
end
|
||||
append(values)
|
||||
return result
|
||||
end
|
||||
local function contains(values, expected)
|
||||
return table.contains(flatten(values), expected)
|
||||
end
|
||||
local function effective_defines(target)
|
||||
return table.join(table.wrap(target:get("defines")),
|
||||
flatten(target:get_from("defines", "dep::*")))
|
||||
end
|
||||
|
||||
for _, name in ipairs({"minirtc", "media", "common", "crossdesk_wire", "rd_log"}) do
|
||||
for _, define in ipairs(effective_defines(targets[name])) do
|
||||
assert(not define:startswith("CROSSDESK_VERSION"), name .. " inherits version macros")
|
||||
assert(not define:startswith("CROSSDESK_PORTABLE"), name .. " inherits portable macros")
|
||||
end
|
||||
end
|
||||
for _, name in ipairs({"gui", "crossdesk", "path_manager"}) do
|
||||
assert(contains(effective_defines(targets[name]), "CROSSDESK_PORTABLE=1") ==
|
||||
(portable == "true"), name .. " has inconsistent portable layout/configuration")
|
||||
end
|
||||
|
||||
local gui = targets.gui
|
||||
local version_files = {
|
||||
["apps/desktop/src/gui/application/gui_application.cpp"] = true,
|
||||
["apps/desktop/src/gui/runtime/peer_event_handler.cpp"] = true
|
||||
}
|
||||
local seen = {}
|
||||
for _, file in ipairs(gui:sourcefiles()) do
|
||||
assert(not seen[file], "duplicate GUI source: " .. file)
|
||||
seen[file] = true
|
||||
local defines = (gui:fileconfig(file) or {}).defines
|
||||
assert(contains(defines, 'CROSSDESK_VERSION="v1.5.3-2-20260913"') ==
|
||||
(version_files[file] == true), "unexpected version scope: " .. file)
|
||||
end
|
||||
for file in pairs(version_files) do
|
||||
assert(seen[file], "missing version consumer: " .. file)
|
||||
end
|
||||
|
||||
if plat == "windows" then
|
||||
local resources = {
|
||||
crossdesk = portable == "true" and "crossdesk_portable" or "crossdesk",
|
||||
crossdesk_service = "crossdesk_service",
|
||||
crossdesk_session_helper = "crossdesk_session_helper",
|
||||
crossdesk_privacy_window = "crossdesk_privacy_window",
|
||||
wgc_plugin = "wgc_plugin"
|
||||
}
|
||||
for name, resource in pairs(resources) do
|
||||
local defines = targets[name]:fileconfig(
|
||||
"apps/desktop/resources/windows/" .. resource .. ".rc").defines
|
||||
assert(contains(defines, 'CROSSDESK_VERSION_STRING="v1.5.3-2-20260913"'))
|
||||
assert(contains(defines, "CROSSDESK_VERSION_NUMERIC=1,5,3,0"))
|
||||
assert(contains(defines, "CROSSDESK_PORTABLE=1") ==
|
||||
(name == "crossdesk_session_helper" and portable == "true"))
|
||||
end
|
||||
end
|
||||
print("Build define scopes passed: %s portable=%s", plat, portable)
|
||||
end
|
||||
@@ -29,30 +29,6 @@ function setup_options_and_dependencies()
|
||||
set_description("Build CrossDesk as a portable package that stores data beside the executable")
|
||||
option_end()
|
||||
|
||||
-- Preserve the existing codebase/toolchain contract. Slint targets opt in to
|
||||
-- C++20 locally because its generated C++ API requires it.
|
||||
local crossdesk_version = get_config("CROSSDESK_VERSION") or "0.0.0"
|
||||
local version_base = crossdesk_version:gsub("^v", ""):match("^(%d[%d%.]*)") or "0.0.0"
|
||||
local version_parts = {}
|
||||
for part in version_base:gmatch("%d+") do
|
||||
local value = tonumber(part) or 0
|
||||
if value > 65535 then
|
||||
value = 0
|
||||
end
|
||||
table.insert(version_parts, value)
|
||||
if #version_parts == 4 then
|
||||
break
|
||||
end
|
||||
end
|
||||
while #version_parts < 4 do
|
||||
table.insert(version_parts, 0)
|
||||
end
|
||||
|
||||
add_defines("CROSSDESK_VERSION_STRING=\"" .. crossdesk_version .. "\"")
|
||||
if is_os("windows") then
|
||||
add_defines("CROSSDESK_VERSION_NUMERIC=" .. table.concat(version_parts, ","))
|
||||
end
|
||||
|
||||
-- set_policy("build.warning", true)
|
||||
-- set_warnings("all", "extra")
|
||||
-- add_cxxflags("/W4", "/WX")
|
||||
@@ -61,10 +37,6 @@ function setup_options_and_dependencies()
|
||||
add_defines("USE_CUDA=" .. (is_config("USE_CUDA", true) and "1" or "0"))
|
||||
add_defines("USE_WAYLAND=" .. (is_config("USE_WAYLAND", true) and "1" or "0"))
|
||||
add_defines("USE_DRM=" .. (is_config("USE_DRM", true) and "1" or "0"))
|
||||
if is_config("CROSSDESK_PORTABLE", true) then
|
||||
add_defines("CROSSDESK_PORTABLE=1")
|
||||
end
|
||||
|
||||
if is_mode("debug") then
|
||||
add_defines("CROSSDESK_DEBUG")
|
||||
end
|
||||
|
||||
@@ -3,6 +3,30 @@ function setup_targets()
|
||||
|
||||
includes("deps/submodules", "deps/thirdparty")
|
||||
|
||||
local crossdesk_version = get_config("CROSSDESK_VERSION") or "0.0.0"
|
||||
local version_define = "CROSSDESK_VERSION=\"" .. crossdesk_version .. "\""
|
||||
local windows_resource_defines = {
|
||||
"CROSSDESK_VERSION_STRING=\"" .. crossdesk_version .. "\""
|
||||
}
|
||||
local version_base = crossdesk_version:gsub("^v", ""):match("^(%d[%d%.]*)") or "0.0.0"
|
||||
local version_parts = {}
|
||||
for part in version_base:gmatch("%d+") do
|
||||
local value = tonumber(part) or 0
|
||||
table.insert(version_parts, value <= 65535 and value or 0)
|
||||
if #version_parts == 4 then
|
||||
break
|
||||
end
|
||||
end
|
||||
while #version_parts < 4 do
|
||||
table.insert(version_parts, 0)
|
||||
end
|
||||
table.insert(windows_resource_defines,
|
||||
"CROSSDESK_VERSION_NUMERIC=" .. table.concat(version_parts, ","))
|
||||
local windows_helper_resource_defines = table.join(windows_resource_defines)
|
||||
if is_config("CROSSDESK_PORTABLE", true) then
|
||||
table.insert(windows_helper_resource_defines, "CROSSDESK_PORTABLE=1")
|
||||
end
|
||||
|
||||
local function copy_slint_runtime(target)
|
||||
if not target:is_plat("windows") then
|
||||
return
|
||||
@@ -50,6 +74,9 @@ function setup_targets()
|
||||
|
||||
target("path_manager")
|
||||
set_kind("object")
|
||||
if is_config("CROSSDESK_PORTABLE", true) then
|
||||
add_defines("CROSSDESK_PORTABLE=1")
|
||||
end
|
||||
add_deps("rd_log")
|
||||
add_files("apps/desktop/src/path_manager/*.cpp")
|
||||
if is_os("windows") then
|
||||
@@ -182,7 +209,8 @@ function setup_targets()
|
||||
set_runtimes("MT")
|
||||
add_files("apps/desktop/src/platform/windows/privacy/band_module/main.cpp")
|
||||
add_files("apps/desktop/src/platform/windows/privacy/band_module/privacy_cover_renderer.cpp")
|
||||
add_files("apps/desktop/resources/windows/crossdesk_privacy_window.rc")
|
||||
add_files("apps/desktop/resources/windows/crossdesk_privacy_window.rc",
|
||||
{defines = windows_resource_defines})
|
||||
add_syslinks("user32", "gdi32", "dwmapi")
|
||||
|
||||
target("crossdesk_privacy_cursor_helper")
|
||||
@@ -321,7 +349,6 @@ function setup_targets()
|
||||
target("version_checker")
|
||||
set_kind("object")
|
||||
add_packages("cpp-httplib")
|
||||
add_defines("CROSSDESK_VERSION=\"" .. (get_config("CROSSDESK_VERSION") or "Unknown") .. "\"")
|
||||
add_deps("rd_log")
|
||||
add_files("apps/desktop/src/version_checker/*.cpp")
|
||||
add_includedirs("apps/desktop/src/version_checker", {public = true})
|
||||
@@ -353,7 +380,11 @@ function setup_targets()
|
||||
add_packages("slint", {public = true})
|
||||
add_packages("libyuv", "tinyfiledialogs")
|
||||
add_rules("slint")
|
||||
add_defines("CROSSDESK_VERSION=\"" .. (get_config("CROSSDESK_VERSION") or "Unknown") .. "\"")
|
||||
if is_config("CROSSDESK_PORTABLE", true) then
|
||||
-- RuntimeState changes layout in portable builds. Consumers of GUI
|
||||
-- headers must see the same definition, without affecting its deps.
|
||||
add_defines("CROSSDESK_PORTABLE=1", {public = true})
|
||||
end
|
||||
add_deps("rd_log", "common", "assets", "config_center", "minirtc",
|
||||
"path_manager", "screen_capturer", "speaker_capturer",
|
||||
"device_controller", "thumbnail", "version_checker", "tools",
|
||||
@@ -365,6 +396,11 @@ function setup_targets()
|
||||
"apps/desktop/src/gui/features/devices/*.cpp", "apps/desktop/src/gui/features/input/*.cpp",
|
||||
"apps/desktop/src/gui/features/clipboard/*.cpp", "apps/desktop/src/gui/features/file_transfer/*.cpp",
|
||||
"apps/desktop/src/gui/features/settings/*.cpp", "apps/desktop/src/gui/ui/crossdesk_ui.slint")
|
||||
-- Only these translation units consume the application version. Keep
|
||||
-- version changes out of the flags for media and the rest of the GUI.
|
||||
add_files("apps/desktop/src/gui/application/gui_application.cpp",
|
||||
"apps/desktop/src/gui/runtime/peer_event_handler.cpp",
|
||||
{defines = version_define})
|
||||
add_includedirs("apps/desktop/src", "apps/desktop/src/gui", {public = true})
|
||||
add_includedirs("apps/desktop/src/platform/common/input")
|
||||
if is_os("windows") then
|
||||
@@ -416,7 +452,8 @@ function setup_targets()
|
||||
add_files("apps/desktop/src/platform/windows/screen_capturer/screen_capturer_wgc.cpp",
|
||||
"apps/desktop/src/platform/windows/screen_capturer/wgc_session_impl.cpp",
|
||||
"apps/desktop/src/platform/windows/screen_capturer/wgc_plugin_entry.cpp")
|
||||
add_files("apps/desktop/resources/windows/wgc_plugin.rc")
|
||||
add_files("apps/desktop/resources/windows/wgc_plugin.rc",
|
||||
{defines = windows_resource_defines})
|
||||
add_includedirs("apps/desktop/src/common", "apps/desktop/src/screen_capturer",
|
||||
"apps/desktop/src/platform/windows/screen_capturer",
|
||||
"deps/submodules/minirtc/src/api")
|
||||
@@ -427,7 +464,8 @@ function setup_targets()
|
||||
add_links("Advapi32", "Wtsapi32", "Ole32", "Userenv")
|
||||
add_files("apps/desktop/src/platform/windows/service/main.cpp",
|
||||
"apps/desktop/src/platform/windows/service/service_host.cpp")
|
||||
add_files("apps/desktop/resources/windows/crossdesk_service.rc")
|
||||
add_files("apps/desktop/resources/windows/crossdesk_service.rc",
|
||||
{defines = windows_resource_defines})
|
||||
add_includedirs("apps/desktop/src/platform/windows/service")
|
||||
|
||||
target("crossdesk_session_helper")
|
||||
@@ -436,7 +474,8 @@ function setup_targets()
|
||||
add_deps("rd_log", "path_manager")
|
||||
add_links("Advapi32", "User32", "Wtsapi32", "Gdi32")
|
||||
add_files("apps/desktop/src/platform/windows/service/session_helper_main.cpp")
|
||||
add_files("apps/desktop/resources/windows/crossdesk_session_helper.rc")
|
||||
add_files("apps/desktop/resources/windows/crossdesk_session_helper.rc",
|
||||
{defines = windows_helper_resource_defines})
|
||||
add_includedirs("apps/desktop/src/common",
|
||||
"apps/desktop/src/platform/windows/service")
|
||||
add_includedirs("apps/desktop/src/platform/windows/input")
|
||||
@@ -453,7 +492,7 @@ function setup_targets()
|
||||
add_includedirs("apps/desktop/src/platform/windows/service")
|
||||
add_links("Advapi32", "Wtsapi32", "Ole32", "Userenv")
|
||||
add_deps("wgc_plugin", "crossdesk_service", "crossdesk_session_helper")
|
||||
add_files(crossdesk_windows_resource)
|
||||
add_files(crossdesk_windows_resource, {defines = windows_resource_defines})
|
||||
elseif is_os("macosx") then
|
||||
add_files("apps/desktop/src/platform/common/posix_daemon_backend.cpp",
|
||||
"apps/desktop/src/platform/macos/daemon_backend.cpp")
|
||||
|
||||
+6
-1
@@ -56,12 +56,17 @@ the desktop-compatible `DisplayN`, `control_audio`, `mouse`, `keyboard`,
|
||||
|
||||
## Video codecs
|
||||
|
||||
Every iOS build includes VideoToolbox, OpenH264, dav1d, libaom, and SVT-AV1. H.264 uses
|
||||
Every iOS build includes VideoToolbox, OpenH264, dav1d, and SVT-AV1. H.264 uses
|
||||
VideoToolbox by default; choose software processing under **设置 → 视频编解码 →
|
||||
处理方式** (Settings → Video codec → Processing mode) to use OpenH264 for H.264.
|
||||
The change takes effect on the next connection. AV1 encoding uses SVT-AV1 and AV1 decoding
|
||||
uses dav1d; VideoToolbox AV1 hardware decoding is currently unsupported.
|
||||
|
||||
The unused libaom backends are excluded by default. To include them for development,
|
||||
set the `MINIRTC_ENABLE_AOM` environment variable (or Xcode user-defined build setting)
|
||||
to `true`. This includes libaom in the merged native archive without changing the
|
||||
AV1 factories' choice of SVT-AV1 and dav1d.
|
||||
|
||||
## Physical-device test checklist
|
||||
|
||||
Run the app from Xcode on a physical device and connect to a current desktop
|
||||
|
||||
@@ -8,6 +8,11 @@ REPO_DIR="${IOS_DIR:h:h}"
|
||||
MINIRTC_DIR="${REPO_DIR}/deps/submodules/minirtc"
|
||||
CONFIG_NAME="${CONFIGURATION:-Debug}"
|
||||
MODE="${CONFIG_NAME:l}"
|
||||
MINIRTC_ENABLE_AOM="${MINIRTC_ENABLE_AOM:-false}"
|
||||
if [[ "${MINIRTC_ENABLE_AOM}" != "true" && "${MINIRTC_ENABLE_AOM}" != "false" ]]; then
|
||||
print -u2 "MINIRTC_ENABLE_AOM must be true or false."
|
||||
exit 64
|
||||
fi
|
||||
|
||||
# Keep iOS dependencies isolated from desktop Xmake packages. Besides avoiding
|
||||
# cross-project cache collisions, this guarantees every archive is compiled
|
||||
@@ -77,17 +82,43 @@ WIRE_MANIFEST_FILE="${WIRE_WORK_DIR}/xmake.lua"
|
||||
WIRE_LIBRARY="${WIRE_BUILD_DIR}/iphoneos/arm64/${MODE}/libcrossdesk_wire.a"
|
||||
|
||||
mkdir -p "${OUTPUT_DIR}" "${WIRE_WORK_DIR}"
|
||||
cp "${REPO_DIR}/xmake.lua" "${WIRE_MANIFEST_FILE}"
|
||||
if ! cmp -s "${REPO_DIR}/xmake.lua" "${WIRE_MANIFEST_FILE}"; then
|
||||
cp "${REPO_DIR}/xmake.lua" "${WIRE_MANIFEST_FILE}"
|
||||
fi
|
||||
|
||||
# Reuse Xmake's compiler/dependency probes unless the build environment changes.
|
||||
# Still configure on every invocation: Xmake must see manifest edits and restore
|
||||
# the iOS configuration if MiniRTC was built standalone for another platform.
|
||||
CONFIG_SIGNATURE="$(print -r -- "${XMAKE_BIN}" "${XMAKE_DEVELOPER_DIR}" \
|
||||
"${MODE}" "${ARCH_NAME}" "16.0" "${XMAKE_PKG_INSTALLDIR}" "${MINIRTC_ENABLE_AOM}"
|
||||
run_xmake --version | sed -n '1p'
|
||||
DEVELOPER_DIR="${XMAKE_DEVELOPER_DIR}" xcodebuild -version
|
||||
DEVELOPER_DIR="${XMAKE_DEVELOPER_DIR}" xcrun --sdk iphoneos --show-sdk-path
|
||||
DEVELOPER_DIR="${XMAKE_DEVELOPER_DIR}" xcrun --sdk iphoneos --show-sdk-version)"
|
||||
|
||||
configure_xmake() {
|
||||
local project_dir="$1"
|
||||
local build_dir="$2"
|
||||
shift 2
|
||||
local stamp="${build_dir}/ios-config-signature"
|
||||
local clean_flags=()
|
||||
if [[ ! -f "${stamp}" || "$(<"${stamp}")" != "${CONFIG_SIGNATURE}" ]]; then
|
||||
clean_flags=(-c)
|
||||
fi
|
||||
run_xmake f -P "${project_dir}" "${clean_flags[@]}" -o "${build_dir}" \
|
||||
-p iphoneos -a arm64 -m "${MODE}" --target_minver=16.0 -y "$@"
|
||||
mkdir -p "${build_dir}"
|
||||
print -r -- "${CONFIG_SIGNATURE}" > "${stamp}"
|
||||
}
|
||||
|
||||
# Xmake stores the configured build directory relative to the process working
|
||||
# directory. Xcode does not guarantee that directory for build phases, so keep
|
||||
# configuration, compilation and inspection anchored to the MiniRTC project.
|
||||
(
|
||||
cd "${MINIRTC_DIR}"
|
||||
run_xmake f -P "${MINIRTC_DIR}" -c -o "${MINIRTC_BUILD_DIR}" \
|
||||
-p iphoneos -a arm64 -m "${MODE}" \
|
||||
--as="${XMAKE_TOOLCHAIN_BIN}/clang" \
|
||||
--target_minver=16.0 --USE_CUDA=false -y
|
||||
configure_xmake "${MINIRTC_DIR}" "${MINIRTC_BUILD_DIR}" \
|
||||
--as="${XMAKE_TOOLCHAIN_BIN}/clang" --USE_CUDA=false \
|
||||
--MINIRTC_ENABLE_AOM="${MINIRTC_ENABLE_AOM}"
|
||||
run_xmake b -P "${MINIRTC_DIR}" minirtc
|
||||
)
|
||||
|
||||
@@ -101,9 +132,7 @@ fi
|
||||
# no second project definition to maintain.
|
||||
(
|
||||
cd "${WIRE_WORK_DIR}"
|
||||
run_xmake f -P "${WIRE_WORK_DIR}" -c -o "${WIRE_BUILD_DIR}" \
|
||||
-p iphoneos -a arm64 -m "${MODE}" \
|
||||
--target_minver=16.0 -y
|
||||
configure_xmake "${WIRE_WORK_DIR}" "${WIRE_BUILD_DIR}"
|
||||
run_xmake b -P "${WIRE_WORK_DIR}" crossdesk_wire
|
||||
)
|
||||
|
||||
@@ -123,8 +152,11 @@ REQUIRED_LINKS=(
|
||||
nice glib-2.0 gobject-2.0 gmodule-2.0 gio-2.0 gthread-2.0 intl
|
||||
gupnp-igd-1.6 gupnp-1.6 gssdp-1.6 soup-3.0 nghttp2 sqlite3 psl xml2
|
||||
ffi pcre2-8 pcre2-posix z ssl crypto srtp2 openfec opus yuv kcp
|
||||
datachannel usrsctp openh264 dav1d aom SvtAv1Enc
|
||||
datachannel usrsctp openh264 dav1d SvtAv1Enc
|
||||
)
|
||||
if [[ "${MINIRTC_ENABLE_AOM}" == "true" ]]; then
|
||||
REQUIRED_LINKS+=(aom)
|
||||
fi
|
||||
DEPENDENCY_ARCHIVES=()
|
||||
|
||||
for link_name in "${REQUIRED_LINKS[@]}"; do
|
||||
@@ -144,10 +176,24 @@ for link_name in "${REQUIRED_LINKS[@]}"; do
|
||||
done
|
||||
|
||||
TEMP_LIBRARY="${OUTPUT_LIBRARY}.tmp"
|
||||
rm -f "${TEMP_LIBRARY}"
|
||||
MERGE_MANIFEST="${OUTPUT_LIBRARY}.inputs.sha256"
|
||||
TEMP_MANIFEST="${MERGE_MANIFEST}.tmp"
|
||||
trap 'rm -f "${TEMP_LIBRARY}" "${TEMP_MANIFEST}"' EXIT
|
||||
|
||||
# Hash contents as well as paths so same-size archives replaced within the same
|
||||
# timestamp tick cannot leave a stale merged library. Preserve the output mtime
|
||||
# on no-op builds to avoid relinking the Xcode app.
|
||||
shasum -a 256 "${SCRIPT_DIR}/build_minirtc_ios.sh" "${MINIRTC_LIBRARY}" \
|
||||
"${DEPENDENCY_ARCHIVES[@]}" "${WIRE_LIBRARY}" > "${TEMP_MANIFEST}"
|
||||
if [[ -f "${OUTPUT_LIBRARY}" ]] && cmp -s "${TEMP_MANIFEST}" "${MERGE_MANIFEST}"; then
|
||||
print "Up to date: ${OUTPUT_LIBRARY}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
/usr/bin/libtool -static -o "${TEMP_LIBRARY}" \
|
||||
"${MINIRTC_LIBRARY}" "${DEPENDENCY_ARCHIVES[@]}" \
|
||||
"${WIRE_LIBRARY}"
|
||||
mv -f "${TEMP_LIBRARY}" "${OUTPUT_LIBRARY}"
|
||||
mv -f "${TEMP_MANIFEST}" "${MERGE_MANIFEST}"
|
||||
|
||||
print "Created ${OUTPUT_LIBRARY}"
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
"""Exercise the native build phase with fake Xmake and real Apple archives."""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
@unittest.skipUnless(sys.platform == "darwin", "requires Apple libtool and zsh")
|
||||
class NativeBuildTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory(prefix="crossdesk-ios-build-")
|
||||
self.addCleanup(self.temp.cleanup)
|
||||
self.root = Path(self.temp.name)
|
||||
self.ios = self.root / "apps/ios"
|
||||
scripts = self.ios / "scripts"
|
||||
scripts.mkdir(parents=True)
|
||||
source = Path(__file__).resolve().parents[1] / "scripts/build_minirtc_ios.sh"
|
||||
self.script = scripts / source.name
|
||||
shutil.copy2(source, self.script)
|
||||
(self.root / "xmake.lua").write_text('-- fixture\n')
|
||||
(self.root / "deps/submodules/minirtc").mkdir(parents=True)
|
||||
self.bin = self.root / "bin"
|
||||
self.bin.mkdir()
|
||||
self.library_dir = self.root / "packages/lib"
|
||||
self.library_dir.mkdir(parents=True)
|
||||
obj = self.root / "fixture.o"
|
||||
subprocess.run(["xcrun", "clang", "-x", "c", "-c", "-", "-o", str(obj)],
|
||||
input="int fixture(void) { return 1; }", text=True, check=True)
|
||||
self.archive = self.root / "fixture.a"
|
||||
subprocess.run(["/usr/bin/libtool", "-static", "-o", str(self.archive), str(obj)],
|
||||
check=True, capture_output=True)
|
||||
links = re.search(r"REQUIRED_LINKS=\((.*?)\)", source.read_text(), re.S)[1].split()
|
||||
for name in links:
|
||||
shutil.copy2(self.archive, self.library_dir / f"lib{name}.a")
|
||||
self.write_tool("xcodebuild", '#!/bin/sh\ncat "$(dirname "$0")/../xcode-version"\n')
|
||||
self.write_tool("xcrun", '#!/bin/sh\necho fixture-iphoneos-sdk\n')
|
||||
(self.root / "xcode-version").write_text("Xcode fixture-1\n")
|
||||
self.write_tool("xmake", f"#!{sys.executable}\n" + r'''
|
||||
import json, pathlib, shutil, sys
|
||||
root = pathlib.Path(__file__).resolve().parents[1]
|
||||
args = sys.argv[1:]
|
||||
with (root / "calls.jsonl").open("a") as log:
|
||||
log.write(json.dumps(args) + "\n")
|
||||
if args[0] == "--version":
|
||||
print("xmake fixture")
|
||||
elif args[0] == "f":
|
||||
if (root / "fail-config").exists():
|
||||
sys.exit(1)
|
||||
project = pathlib.Path(args[args.index("-P") + 1])
|
||||
(project / "config.json").write_text(json.dumps(args))
|
||||
elif args[0] == "b":
|
||||
project = pathlib.Path(args[args.index("-P") + 1])
|
||||
config = json.loads((project / "config.json").read_text())
|
||||
output = pathlib.Path(config[config.index("-o") + 1])
|
||||
output /= "iphoneos/arm64/" + config[config.index("-m") + 1]
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
library = output / ("lib" + args[-1] + ".a")
|
||||
if not library.exists():
|
||||
shutil.copy2(root / "fixture.a", library)
|
||||
elif args[0] == "show":
|
||||
print(" -> " + str(root / "packages/lib") + " -> package fixture")
|
||||
''')
|
||||
self.env = dict(os.environ, PATH=f"{self.bin}:{os.environ['PATH']}",
|
||||
XMAKE_BIN=str(self.bin / "xmake"), CONFIGURATION="Release",
|
||||
MINIRTC_ENABLE_AOM="false",
|
||||
CURRENT_ARCH="arm64", DEVELOPER_DIR=subprocess.check_output(
|
||||
["xcode-select", "-p"], text=True).strip())
|
||||
self.output = self.ios / "Vendor/iphoneos/Release/libCrossDeskMiniRTC.a"
|
||||
|
||||
def write_tool(self, name, contents):
|
||||
path = self.bin / name
|
||||
path.write_text(contents)
|
||||
path.chmod(0o755)
|
||||
|
||||
def run_build(self, success=True):
|
||||
result = subprocess.run(["/bin/zsh", str(self.script)], env=self.env,
|
||||
text=True, capture_output=True)
|
||||
if success:
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
else:
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
return result
|
||||
|
||||
def config_calls(self):
|
||||
import json
|
||||
return [args for line in (self.root / "calls.jsonl").read_text().splitlines()
|
||||
if (args := json.loads(line))[0] == "f"]
|
||||
|
||||
def test_noop_preserves_archives_and_manifest(self):
|
||||
self.run_build()
|
||||
manifest = self.ios / ".xmake/wire-work/xmake.lua"
|
||||
old_times = (self.output.stat().st_mtime_ns, manifest.stat().st_mtime_ns)
|
||||
self.run_build()
|
||||
self.assertEqual(old_times, (self.output.stat().st_mtime_ns, manifest.stat().st_mtime_ns))
|
||||
self.assertEqual(["-c" in args for args in self.config_calls()], [True, True, False, False])
|
||||
|
||||
def test_archive_content_change_with_same_size_and_mtime_is_merged(self):
|
||||
self.run_build()
|
||||
original = self.output.read_bytes()
|
||||
library = self.library_dir / "libopus.a"
|
||||
stat = library.stat()
|
||||
data = library.read_bytes()
|
||||
# Rename an archive member without changing the archive layout or size.
|
||||
self.assertIn(b"fixture.o", data)
|
||||
library.write_bytes(data.replace(b"fixture.o", b"changed.o"))
|
||||
os.utime(library, ns=(stat.st_atime_ns, stat.st_mtime_ns))
|
||||
self.run_build()
|
||||
self.assertNotEqual(original, self.output.read_bytes())
|
||||
|
||||
def test_toolchain_and_mode_changes_clear_probes(self):
|
||||
self.run_build()
|
||||
(self.root / "xcode-version").write_text("Xcode fixture-2\n")
|
||||
self.run_build()
|
||||
self.env["CONFIGURATION"] = "Debug"
|
||||
self.run_build()
|
||||
self.assertEqual(["-c" in args for args in self.config_calls()], [True] * 6)
|
||||
self.assertTrue((self.ios / "Vendor/iphoneos/Debug/libCrossDeskMiniRTC.a").exists())
|
||||
|
||||
def test_failed_configuration_does_not_commit_signature(self):
|
||||
self.run_build()
|
||||
stamp = self.ios / ".xmake/minirtc-build/ios-config-signature"
|
||||
original = stamp.read_bytes()
|
||||
(self.root / "xcode-version").write_text("Xcode fixture-2\n")
|
||||
(self.root / "fail-config").touch()
|
||||
self.run_build(success=False)
|
||||
self.assertEqual(stamp.read_bytes(), original)
|
||||
(self.root / "fail-config").unlink()
|
||||
self.run_build()
|
||||
self.assertTrue(all("-c" in args for args in self.config_calls()))
|
||||
|
||||
def test_aom_is_only_required_and_merged_when_enabled(self):
|
||||
self.run_build()
|
||||
manifest = Path(str(self.output) + ".inputs.sha256")
|
||||
self.assertNotIn("libaom.a", manifest.read_text())
|
||||
original = self.output.read_bytes()
|
||||
self.env["MINIRTC_ENABLE_AOM"] = "true"
|
||||
result = self.run_build(success=False)
|
||||
self.assertIn("Unable to resolve static dependency libaom.a", result.stderr)
|
||||
self.assertEqual(self.output.read_bytes(), original)
|
||||
shutil.copy2(self.archive, self.library_dir / "libaom.a")
|
||||
self.run_build()
|
||||
self.assertIn("libaom.a", manifest.read_text())
|
||||
self.env["MINIRTC_ENABLE_AOM"] = "false"
|
||||
self.run_build()
|
||||
self.assertNotIn("libaom.a", manifest.read_text())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Vendored
+1
-1
Submodule deps/submodules/minirtc updated: 32b689b170...f169a696cb
@@ -131,6 +131,7 @@ PipeWire 不属于强制运行时依赖:程序在运行时检测宿主系统
|
||||
--USE_DRM=true/false: 在 Linux 上启用 DRM 捕获,默认不启用
|
||||
--CROSSDESK_PORTABLE=true/false: 构建便携版本,默认不启用
|
||||
--CROSSDESK_VERSION=xxx: 指定 CrossDesk 的版本
|
||||
--MINIRTC_ENABLE_AOM=true/false: 编译可选 libaom 后端,默认不启用;AV1 仍使用 SVT-AV1 / dav1d
|
||||
|
||||
# 示例
|
||||
xmake f --CROSSDESK_VERSION=1.4.3 --USE_CUDA=true
|
||||
|
||||
Reference in New Issue
Block a user