🐛 Better compatibility with Linux

This commit is contained in:
Tw93
2025-09-05 15:14:55 +08:00
parent 14f337fe2b
commit 9c36a998b4
3 changed files with 126 additions and 51 deletions

13
bin/helpers/merge.ts vendored
View File

@@ -66,10 +66,8 @@ export async function mergeConfig(
const { platform } = process;
// Platform-specific hide_on_close behavior: macOS keeps true, others default to false
const platformHideOnClose = hideOnClose ?? platform === 'darwin';
// Set Windows parameters.
const tauriConfWindowOptions = {
width,
height,
@@ -92,11 +90,14 @@ export async function mergeConfig(
tauriConf.identifier = identifier;
tauriConf.version = appVersion;
if (platform === 'linux') {
tauriConf.mainBinaryName = `pake-${name.toLowerCase()}`;
}
if (platform == 'win32') {
tauriConf.bundle.windows.wix.language[0] = installerLanguage;
}
//Judge the type of URL, whether it is a file or a website.
const pathExists = await fsExtra.pathExists(url);
if (pathExists) {
logger.warn('✼ Your input might be a local file.');
@@ -160,8 +161,8 @@ Version=1.0
Type=Application
Name=${name}
Comment=${name}
Exec=${appNameLower}
Icon=${appNameLower}
Exec=pake-${appNameLower}
Icon=${appNameLower}_512
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml_xml;
StartupNotify=true
@@ -252,7 +253,7 @@ StartupNotify=true
}
if (updateIconPath) {
tauriConf.bundle.icon = [options.icon];
tauriConf.bundle.icon = [iconInfo.path];
} else {
logger.warn(`✼ Icon will remain as default.`);
}

151
dist/cli.js vendored
View File

@@ -350,9 +350,7 @@ async function mergeConfig(url, options, tauriConf) {
}));
const { width, height, fullscreen, hideTitleBar, alwaysOnTop, appVersion, darkMode, disabledWebShortcuts, activationShortcut, userAgent, showSystemTray, systemTrayIcon, useLocalFile, identifier, name, resizable = true, inject, proxyUrl, installerLanguage, hideOnClose, incognito, title, wasm, enableDragDrop, } = options;
const { platform } = process;
// Platform-specific hide_on_close behavior: macOS keeps true, others default to false
const platformHideOnClose = hideOnClose ?? platform === 'darwin';
// Set Windows parameters.
const tauriConfWindowOptions = {
width,
height,
@@ -373,10 +371,12 @@ async function mergeConfig(url, options, tauriConf) {
tauriConf.productName = name;
tauriConf.identifier = identifier;
tauriConf.version = appVersion;
if (platform === 'linux') {
tauriConf.mainBinaryName = `pake-${name.toLowerCase()}`;
}
if (platform == 'win32') {
tauriConf.bundle.windows.wix.language[0] = installerLanguage;
}
//Judge the type of URL, whether it is a file or a website.
const pathExists = await fsExtra.pathExists(url);
if (pathExists) {
logger.warn('✼ Your input might be a local file.');
@@ -427,8 +427,8 @@ Version=1.0
Type=Application
Name=${name}
Comment=${name}
Exec=${appNameLower}
Icon=${appNameLower}
Exec=pake-${appNameLower}
Icon=${appNameLower}_512
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml_xml;
StartupNotify=true
@@ -510,7 +510,7 @@ StartupNotify=true
}
}
if (updateIconPath) {
tauriConf.bundle.icon = [options.icon];
tauriConf.bundle.icon = [iconInfo.path];
}
else {
logger.warn(`✼ Icon will remain as default.`);
@@ -606,15 +606,13 @@ class BaseBuilder {
return process.platform === 'win32' ? 600000 : 300000;
}
getBuildTimeout() {
return 900000; // 15 minutes for all builds
return 900000;
}
async detectPackageManager() {
// 使用缓存避免重复检测
if (BaseBuilder.packageManagerCache) {
return BaseBuilder.packageManagerCache;
}
const { execa } = await import('execa');
// 优先使用pnpm如果可用
try {
await execa('pnpm', ['--version'], { stdio: 'ignore' });
logger.info('✺ Using pnpm for package management.');
@@ -622,7 +620,6 @@ class BaseBuilder {
return 'pnpm';
}
catch {
// pnpm不可用回退到npm
try {
await execa('npm', ['--version'], { stdio: 'ignore' });
logger.info('✺ pnpm not available, using npm for package management.');
@@ -710,9 +707,18 @@ class BaseBuilder {
const appPath = this.getBuildAppPath(npmDirectory, fileName, fileType);
const distPath = path.resolve(`${name}.${fileType}`);
await fsExtra.copy(appPath, distPath);
// Copy raw binary if requested
if (this.options.keepBinary) {
await this.copyRawBinary(npmDirectory, name);
}
await fsExtra.remove(appPath);
logger.success('✔ Build success!');
logger.success('✔ App installer located in', distPath);
// Log binary location if preserved
if (this.options.keepBinary) {
const binaryPath = this.getRawBinaryPath(name);
logger.success('✔ Raw binary located in', path.resolve(binaryPath));
}
}
getFileType(target) {
return target;
@@ -804,6 +810,67 @@ class BaseBuilder {
const bundleDir = fileType.toLowerCase() === 'app' ? 'macos' : fileType.toLowerCase();
return path.join(npmDirectory, this.getBasePath(), bundleDir, `${fileName}.${fileType}`);
}
/**
* Copy raw binary file to output directory
*/
async copyRawBinary(npmDirectory, appName) {
const binaryPath = this.getRawBinarySourcePath(npmDirectory, appName);
const outputPath = this.getRawBinaryPath(appName);
if (await fsExtra.pathExists(binaryPath)) {
await fsExtra.copy(binaryPath, outputPath);
// Make binary executable on Unix-like systems
if (process.platform !== 'win32') {
await fsExtra.chmod(outputPath, 0o755);
}
}
else {
logger.warn(`✼ Raw binary not found at ${binaryPath}, skipping...`);
}
}
/**
* Get the source path of the raw binary file in the build directory
*/
getRawBinarySourcePath(npmDirectory, appName) {
const basePath = this.options.debug ? 'debug' : 'release';
const binaryName = this.getBinaryName(appName);
// Handle cross-platform builds
if (this.options.multiArch || this.hasArchSpecificTarget()) {
return path.join(npmDirectory, this.getArchSpecificPath(), basePath, binaryName);
}
return path.join(npmDirectory, 'src-tauri/target', basePath, binaryName);
}
/**
* Get the output path for the raw binary file
*/
getRawBinaryPath(appName) {
const extension = process.platform === 'win32' ? '.exe' : '';
const suffix = process.platform === 'win32' ? '' : '-binary';
return `${appName}${suffix}${extension}`;
}
/**
* Get the binary name based on app name and platform
*/
getBinaryName(appName) {
const extension = process.platform === 'win32' ? '.exe' : '';
// Linux uses the unique binary name we set in merge.ts
if (process.platform === 'linux') {
return `pake-${appName.toLowerCase()}${extension}`;
}
// Windows and macOS use 'pake' as binary name
return `pake${extension}`;
}
/**
* Check if this build has architecture-specific target
*/
hasArchSpecificTarget() {
return false; // Override in subclasses if needed
}
/**
* Get architecture-specific path for binary
*/
getArchSpecificPath() {
return 'src-tauri/target'; // Override in subclasses if needed
}
}
BaseBuilder.packageManagerCache = null;
// 架构映射配置
@@ -832,31 +899,23 @@ BaseBuilder.ARCH_DISPLAY_NAMES = {
class MacBuilder extends BaseBuilder {
constructor(options) {
super(options);
// Store the original targets value for architecture selection
// For macOS, targets can be architecture names or format names
// Filter out non-architecture values
const validArchs = ['intel', 'apple', 'universal', 'auto', 'x64', 'arm64'];
this.buildArch = validArchs.includes(options.targets || '')
? options.targets
: 'auto';
// Use DMG by default for distribution
// Only create app bundles for testing to avoid user interaction
if (process.env.PAKE_CREATE_APP === '1') {
this.buildFormat = 'app';
}
else {
this.buildFormat = 'dmg';
}
// Set targets to format for Tauri
this.options.targets = this.buildFormat;
}
getFileName() {
const { name } = this.options;
// For app bundles, use simple name without version/arch
if (this.buildFormat === 'app') {
return name;
}
// For DMG files, use versioned filename
let arch;
if (this.buildArch === 'universal' || this.options.multiArch) {
arch = 'universal';
@@ -868,7 +927,6 @@ class MacBuilder extends BaseBuilder {
arch = 'x64';
}
else {
// Auto-detect based on current architecture
arch = this.getArchDisplayName(this.resolveTargetArch(this.buildArch));
}
return `${name}_${tauriConfig.version}_${arch}`;
@@ -893,7 +951,6 @@ class MacBuilder extends BaseBuilder {
throw new Error(`Unsupported architecture: ${actualArch} for macOS`);
}
let fullCommand = this.buildBaseCommand(packageManager, configPath, buildTarget);
// Add features
const features = this.getBuildFeatures();
if (features.length > 0) {
fullCommand += ` --features ${features.join(',')}`;
@@ -906,14 +963,20 @@ class MacBuilder extends BaseBuilder {
const target = this.getTauriTarget(actualArch, 'darwin');
return `src-tauri/target/${target}/${basePath}/bundle`;
}
hasArchSpecificTarget() {
return true;
}
getArchSpecificPath() {
const actualArch = this.getActualArch();
const target = this.getTauriTarget(actualArch, 'darwin');
return `src-tauri/target/${target}`;
}
}
class WinBuilder extends BaseBuilder {
constructor(options) {
super(options);
this.buildFormat = 'msi';
// For Windows, targets can be architecture names or format names
// Filter out non-architecture values
const validArchs = ['x64', 'arm64', 'auto'];
this.buildArch = validArchs.includes(options.targets || '')
? this.resolveTargetArch(options.targets)
@@ -933,7 +996,6 @@ class WinBuilder extends BaseBuilder {
throw new Error(`Unsupported architecture: ${this.buildArch} for Windows`);
}
let fullCommand = this.buildBaseCommand(packageManager, configPath, buildTarget);
// Add features
const features = this.getBuildFeatures();
if (features.length > 0) {
fullCommand += ` --features ${features.join(',')}`;
@@ -945,12 +1007,18 @@ class WinBuilder extends BaseBuilder {
const target = this.getTauriTarget(this.buildArch, 'win32');
return `src-tauri/target/${target}/${basePath}/bundle/`;
}
hasArchSpecificTarget() {
return true;
}
getArchSpecificPath() {
const target = this.getTauriTarget(this.buildArch, 'win32');
return `src-tauri/target/${target}`;
}
}
class LinuxBuilder extends BaseBuilder {
constructor(options) {
super(options);
// Parse target format and architecture
const target = options.targets || 'deb';
if (target.includes('-arm64')) {
this.buildFormat = target.replace('-arm64', '');
@@ -960,33 +1028,32 @@ class LinuxBuilder extends BaseBuilder {
this.buildFormat = target;
this.buildArch = this.resolveTargetArch('auto');
}
// Set targets to format for Tauri
this.options.targets = this.buildFormat;
}
getFileName() {
const { name, targets } = this.options;
const version = tauriConfig.version;
// Determine architecture display name
let arch;
if (this.buildArch === 'arm64') {
arch = targets === 'rpm' || targets === 'appimage' ? 'aarch64' : 'arm64';
}
else {
// Auto-detect or default to current architecture
const resolvedArch = this.buildArch === 'x64' ? 'amd64' : this.buildArch;
arch = resolvedArch;
if (resolvedArch === 'arm64' &&
(targets === 'rpm' || targets === 'appimage')) {
arch = 'aarch64';
if (this.buildArch === 'x64') {
arch = targets === 'rpm' ? 'x86_64' : 'amd64';
}
else {
arch = this.buildArch;
if (this.buildArch === 'arm64' &&
(targets === 'rpm' || targets === 'appimage')) {
arch = 'aarch64';
}
}
}
// The RPM format uses different separators and version number formats
if (targets === 'rpm') {
return `${name}-${version}-1.${arch}`;
}
return `${name}_${version}_${arch}`;
}
// Customize it, considering that there are all targets.
async build(url) {
const targetTypes = ['deb', 'appimage', 'rpm'];
for (const target of targetTypes) {
@@ -997,12 +1064,10 @@ class LinuxBuilder extends BaseBuilder {
}
getBuildCommand(packageManager = 'pnpm') {
const configPath = path.join('src-tauri', '.pake', 'tauri.conf.json');
// Only add target if it's ARM64
const buildTarget = this.buildArch === 'arm64'
? this.getTauriTarget(this.buildArch, 'linux')
: undefined;
let fullCommand = this.buildBaseCommand(packageManager, configPath, buildTarget);
// Add features
const features = this.getBuildFeatures();
if (features.length > 0) {
fullCommand += ` --features ${features.join(',')}`;
@@ -1023,6 +1088,16 @@ class LinuxBuilder extends BaseBuilder {
}
return super.getFileType(target);
}
hasArchSpecificTarget() {
return this.buildArch === 'arm64';
}
getArchSpecificPath() {
if (this.buildArch === 'arm64') {
const target = this.getTauriTarget(this.buildArch, 'linux');
return `src-tauri/target/${target}`;
}
return super.getArchSpecificPath();
}
}
const { platform } = process;
@@ -1066,6 +1141,7 @@ const DEFAULT_PAKE_OPTIONS = {
incognito: false,
wasm: false,
enableDragDrop: false,
keepBinary: false,
};
async function checkUpdateTips() {
@@ -1523,6 +1599,9 @@ program
.addOption(new Option('--enable-drag-drop', 'Enable drag and drop functionality')
.default(DEFAULT_PAKE_OPTIONS.enableDragDrop)
.hideHelp())
.addOption(new Option('--keep-binary', 'Keep raw binary file alongside installer')
.default(DEFAULT_PAKE_OPTIONS.keepBinary)
.hideHelp())
.addOption(new Option('--installer-language <string>', 'Installer language')
.default(DEFAULT_PAKE_OPTIONS.installerLanguage)
.hideHelp())

View File

@@ -40,7 +40,6 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
.always_on_top(window_config.always_on_top)
.incognito(window_config.incognito);
// Conditionally disable drag-drop handler
if !window_config.enable_drag_drop {
window_builder = window_builder.disable_drag_drop_handler();
}
@@ -52,14 +51,12 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
.initialization_script(include_str!("../inject/style.js"))
.initialization_script(include_str!("../inject/custom.js"));
// Configure WASM support with required headers for SharedArrayBuffer
if window_config.enable_wasm {
window_builder = window_builder
.additional_browser_args("--enable-features=SharedArrayBuffer")
.additional_browser_args("--enable-unsafe-webgpu");
}
// Configure proxy if specified
if !config.proxy_url.is_empty() {
if let Ok(proxy_url) = Url::from_str(&config.proxy_url) {
window_builder = window_builder.proxy_url(proxy_url);
@@ -86,10 +83,9 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
{
window_builder = window_builder
.data_directory(_data_dir)
.title(app.package_info().name.clone());
.title(app.package_info().name.clone())
.additional_browser_args("--disable-blink-features=AutomationControlled");
// Set theme to None for automatic system theme detection on Windows
// This allows the window to respond to system theme changes automatically
window_builder = window_builder.theme(None);
}
@@ -97,10 +93,9 @@ pub fn set_window(app: &mut App, config: &PakeConfig, tauri_config: &Config) ->
{
window_builder = window_builder
.data_directory(_data_dir)
.title(app.package_info().name.clone());
.title(app.package_info().name.clone())
.additional_browser_args("--disable-blink-features=AutomationControlled");
// Set theme to None for automatic system theme detection on Linux
// This allows the window to respond to system theme changes automatically
window_builder = window_builder.theme(None);
}