import { chmod, cp, mkdir, rm, writeFile } from 'node:fs/promises' import { arch, platform } from 'node:os' import { join, resolve } from 'node:path' import { spawnSync } from 'node:child_process' const root = resolve(import.meta.dirname, '..') const native = join(root, 'connector', 'native') const releaseRoot = join(root, 'release', 'connector') const commonSources = [ 'json.cpp', 'encoding.cpp', 'zip.cpp', 'config.cpp', 'platform_common.cpp', 'process_posix.cpp', 'process_windows.cpp', 'workspace.cpp', 'http_server.cpp', 'connector_service.cpp', ].map((name) => join(native, name)) function run(command, args) { const result = spawnSync(command, args, { cwd: root, stdio: 'inherit', shell: false }) if (result.error) throw result.error if (result.status !== 0) throw new Error(`${command} 构建失败(退出码 ${result.status})`) } async function buildMac() { const packageDir = join(releaseRoot, `macos-${arch()}`) const app = join(packageDir, 'Spine粒子连接器.app') const executable = join(app, 'Contents', 'MacOS', 'SpineParticleConnector') await rm(packageDir, { recursive: true, force: true }) await mkdir(join(app, 'Contents', 'MacOS'), { recursive: true }) await mkdir(join(app, 'Contents', 'Resources'), { recursive: true }) run(process.env.CXX || 'clang++', [ '-std=c++17', '-O2', '-Wall', '-Wextra', '-Werror', '-fobjc-arc', ...commonSources, join(native, 'app_macos.mm'), '-framework', 'AppKit', '-framework', 'Foundation', '-o', executable, ]) await chmod(executable, 0o755) await writeFile(join(app, 'Contents', 'Info.plist'), ` CFBundleDisplayNameSpine粒子连接器 CFBundleExecutableSpineParticleConnector CFBundleIdentifiercom.spineparticle.connector CFBundleInfoDictionaryVersion6.0 CFBundleNameSpine粒子连接器 CFBundlePackageTypeAPPL CFBundleShortVersionString2.0.0 CFBundleVersion2.0.0 LSMinimumSystemVersion11.0 NSHighResolutionCapable `, 'utf8') await cp(join(root, 'connector', 'DISTRIBUTION.md'), join(packageDir, '使用说明.md')) console.log(`已生成 macOS 应用:${app}`) } async function buildWindows() { const packageDir = join(releaseRoot, `windows-${arch()}`) const executable = join(packageDir, 'Spine粒子连接器.exe') await rm(packageDir, { recursive: true, force: true }) await mkdir(packageDir, { recursive: true }) const compiler = process.env.CXX || 'cl.exe' if (/cl(?:\.exe)?$/i.test(compiler)) { run(compiler, ['/nologo', '/std:c++17', '/O2', '/EHsc', '/utf-8', '/W4', ...commonSources, join(native, 'app_windows.cpp'), '/link', '/SUBSYSTEM:WINDOWS', `'/OUT:${executable}'`.replaceAll("'", ''), 'ws2_32.lib', 'comdlg32.lib', 'user32.lib', 'gdi32.lib']) } else { run(compiler, ['-std=c++17', '-O2', '-Wall', '-Wextra', '-Werror', ...commonSources, join(native, 'app_windows.cpp'), '-municode', '-mwindows', '-lws2_32', '-lcomdlg32', '-o', executable]) } await cp(join(root, 'connector', 'DISTRIBUTION.md'), join(packageDir, '使用说明.md')) console.log(`已生成 Windows 应用:${executable}`) } if (platform() === 'darwin') await buildMac() else if (platform() === 'win32') await buildWindows() else throw new Error('桌面连接器 App 当前支持 macOS 和 Windows')