✨ Support keep-binary usage
This commit is contained in:
99
bin/builders/BaseBuilder.ts
vendored
99
bin/builders/BaseBuilder.ts
vendored
@@ -37,25 +37,22 @@ export default abstract class BaseBuilder {
|
||||
}
|
||||
|
||||
private getBuildTimeout(): number {
|
||||
return 900000; // 15 minutes for all builds
|
||||
return 900000;
|
||||
}
|
||||
|
||||
private async detectPackageManager(): Promise<string> {
|
||||
// 使用缓存避免重复检测
|
||||
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.');
|
||||
BaseBuilder.packageManagerCache = 'pnpm';
|
||||
return 'pnpm';
|
||||
} catch {
|
||||
// pnpm不可用,回退到npm
|
||||
try {
|
||||
await execa('npm', ['--version'], { stdio: 'ignore' });
|
||||
logger.info('✺ pnpm not available, using npm for package management.');
|
||||
@@ -176,9 +173,21 @@ export default abstract 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));
|
||||
}
|
||||
}
|
||||
|
||||
protected getFileType(target: string): string {
|
||||
@@ -338,4 +347,86 @@ export default abstract class BaseBuilder {
|
||||
`${fileName}.${fileType}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy raw binary file to output directory
|
||||
*/
|
||||
protected async copyRawBinary(
|
||||
npmDirectory: string,
|
||||
appName: string,
|
||||
): Promise<void> {
|
||||
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
|
||||
*/
|
||||
protected getRawBinarySourcePath(
|
||||
npmDirectory: string,
|
||||
appName: string,
|
||||
): string {
|
||||
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
|
||||
*/
|
||||
protected getRawBinaryPath(appName: string): string {
|
||||
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
|
||||
*/
|
||||
protected getBinaryName(appName: string): string {
|
||||
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
|
||||
*/
|
||||
protected hasArchSpecificTarget(): boolean {
|
||||
return false; // Override in subclasses if needed
|
||||
}
|
||||
|
||||
/**
|
||||
* Get architecture-specific path for binary
|
||||
*/
|
||||
protected getArchSpecificPath(): string {
|
||||
return 'src-tauri/target'; // Override in subclasses if needed
|
||||
}
|
||||
}
|
||||
|
||||
37
bin/builders/LinuxBuilder.ts
vendored
37
bin/builders/LinuxBuilder.ts
vendored
@@ -10,7 +10,6 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
constructor(options: PakeAppOptions) {
|
||||
super(options);
|
||||
|
||||
// Parse target format and architecture
|
||||
const target = options.targets || 'deb';
|
||||
if (target.includes('-arm64')) {
|
||||
this.buildFormat = target.replace('-arm64', '');
|
||||
@@ -20,7 +19,6 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
this.buildArch = this.resolveTargetArch('auto');
|
||||
}
|
||||
|
||||
// Set targets to format for Tauri
|
||||
this.options.targets = this.buildFormat;
|
||||
}
|
||||
|
||||
@@ -28,23 +26,23 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
const { name, targets } = this.options;
|
||||
const version = tauriConfig.version;
|
||||
|
||||
// Determine architecture display name
|
||||
let arch: string;
|
||||
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}`;
|
||||
}
|
||||
@@ -52,7 +50,6 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
return `${name}_${version}_${arch}`;
|
||||
}
|
||||
|
||||
// Customize it, considering that there are all targets.
|
||||
async build(url: string) {
|
||||
const targetTypes = ['deb', 'appimage', 'rpm'];
|
||||
for (const target of targetTypes) {
|
||||
@@ -65,7 +62,6 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
protected getBuildCommand(packageManager: string = 'pnpm'): string {
|
||||
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')
|
||||
@@ -77,7 +73,6 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
buildTarget,
|
||||
);
|
||||
|
||||
// Add features
|
||||
const features = this.getBuildFeatures();
|
||||
if (features.length > 0) {
|
||||
fullCommand += ` --features ${features.join(',')}`;
|
||||
@@ -103,4 +98,16 @@ export default class LinuxBuilder extends BaseBuilder {
|
||||
}
|
||||
return super.getFileType(target);
|
||||
}
|
||||
|
||||
protected hasArchSpecificTarget(): boolean {
|
||||
return this.buildArch === 'arm64';
|
||||
}
|
||||
|
||||
protected getArchSpecificPath(): string {
|
||||
if (this.buildArch === 'arm64') {
|
||||
const target = this.getTauriTarget(this.buildArch, 'linux');
|
||||
return `src-tauri/target/${target}`;
|
||||
}
|
||||
return super.getArchSpecificPath();
|
||||
}
|
||||
}
|
||||
|
||||
20
bin/builders/MacBuilder.ts
vendored
20
bin/builders/MacBuilder.ts
vendored
@@ -10,35 +10,27 @@ export default class MacBuilder extends BaseBuilder {
|
||||
constructor(options: PakeAppOptions) {
|
||||
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(): string {
|
||||
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: string;
|
||||
if (this.buildArch === 'universal' || this.options.multiArch) {
|
||||
arch = 'universal';
|
||||
@@ -47,7 +39,6 @@ export default class MacBuilder extends BaseBuilder {
|
||||
} else if (this.buildArch === 'intel') {
|
||||
arch = 'x64';
|
||||
} else {
|
||||
// Auto-detect based on current architecture
|
||||
arch = this.getArchDisplayName(this.resolveTargetArch(this.buildArch));
|
||||
}
|
||||
return `${name}_${tauriConfig.version}_${arch}`;
|
||||
@@ -79,7 +70,6 @@ export default class MacBuilder extends BaseBuilder {
|
||||
buildTarget,
|
||||
);
|
||||
|
||||
// Add features
|
||||
const features = this.getBuildFeatures();
|
||||
if (features.length > 0) {
|
||||
fullCommand += ` --features ${features.join(',')}`;
|
||||
@@ -95,4 +85,14 @@ export default class MacBuilder extends BaseBuilder {
|
||||
|
||||
return `src-tauri/target/${target}/${basePath}/bundle`;
|
||||
}
|
||||
|
||||
protected hasArchSpecificTarget(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected getArchSpecificPath(): string {
|
||||
const actualArch = this.getActualArch();
|
||||
const target = this.getTauriTarget(actualArch, 'darwin');
|
||||
return `src-tauri/target/${target}`;
|
||||
}
|
||||
}
|
||||
|
||||
12
bin/builders/WinBuilder.ts
vendored
12
bin/builders/WinBuilder.ts
vendored
@@ -9,8 +9,6 @@ export default class WinBuilder extends BaseBuilder {
|
||||
|
||||
constructor(options: PakeAppOptions) {
|
||||
super(options);
|
||||
// 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)
|
||||
@@ -41,7 +39,6 @@ export default class WinBuilder extends BaseBuilder {
|
||||
buildTarget,
|
||||
);
|
||||
|
||||
// Add features
|
||||
const features = this.getBuildFeatures();
|
||||
if (features.length > 0) {
|
||||
fullCommand += ` --features ${features.join(',')}`;
|
||||
@@ -55,4 +52,13 @@ export default class WinBuilder extends BaseBuilder {
|
||||
const target = this.getTauriTarget(this.buildArch, 'win32');
|
||||
return `src-tauri/target/${target}/${basePath}/bundle/`;
|
||||
}
|
||||
|
||||
protected hasArchSpecificTarget(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected getArchSpecificPath(): string {
|
||||
const target = this.getTauriTarget(this.buildArch, 'win32');
|
||||
return `src-tauri/target/${target}`;
|
||||
}
|
||||
}
|
||||
|
||||
5
bin/cli.ts
vendored
5
bin/cli.ts
vendored
@@ -148,6 +148,11 @@ program
|
||||
.default(DEFAULT.enableDragDrop)
|
||||
.hideHelp(),
|
||||
)
|
||||
.addOption(
|
||||
new Option('--keep-binary', 'Keep raw binary file alongside installer')
|
||||
.default(DEFAULT.keepBinary)
|
||||
.hideHelp(),
|
||||
)
|
||||
.addOption(
|
||||
new Option('--installer-language <string>', 'Installer language')
|
||||
.default(DEFAULT.installerLanguage)
|
||||
|
||||
1
bin/defaults.ts
vendored
1
bin/defaults.ts
vendored
@@ -26,6 +26,7 @@ export const DEFAULT_PAKE_OPTIONS: PakeCliOptions = {
|
||||
incognito: false,
|
||||
wasm: false,
|
||||
enableDragDrop: false,
|
||||
keepBinary: false,
|
||||
};
|
||||
|
||||
// Just for cli development
|
||||
|
||||
3
bin/types.ts
vendored
3
bin/types.ts
vendored
@@ -84,6 +84,9 @@ export interface PakeCliOptions {
|
||||
|
||||
// Enable drag and drop functionality, default false
|
||||
enableDragDrop: boolean;
|
||||
|
||||
// Keep raw binary file alongside installer, default false
|
||||
keepBinary: boolean;
|
||||
}
|
||||
|
||||
export interface PakeAppOptions extends PakeCliOptions {
|
||||
|
||||
Reference in New Issue
Block a user