210 lines
5.9 KiB
TypeScript
210 lines
5.9 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
import * as log from 'loglevel';
|
|
|
|
import { NativefierOptions } from '../../../shared/src/options/model';
|
|
import { getVersionString } from './rceditGet';
|
|
import { fileExists } from '../fsHelpers';
|
|
type ExecutableInfo = {
|
|
arch?: string;
|
|
};
|
|
|
|
function getExecutableBytes(executablePath: string): Uint8Array {
|
|
return fs.readFileSync(executablePath);
|
|
}
|
|
|
|
function getExecutableArch(
|
|
exeBytes: Uint8Array,
|
|
platform: string,
|
|
): string | undefined {
|
|
switch (platform) {
|
|
case 'linux':
|
|
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
|
|
switch (exeBytes[0x12]) {
|
|
case 0x03:
|
|
return 'ia32';
|
|
case 0x28:
|
|
return 'armv7l';
|
|
case 0x3e:
|
|
return 'x64';
|
|
case 0xb7:
|
|
return 'arm64';
|
|
default:
|
|
return undefined;
|
|
}
|
|
case 'darwin':
|
|
case 'mas':
|
|
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
|
|
|
|
switch ((exeBytes[0x04] << 8) + exeBytes[0x05]) {
|
|
case 0x0700:
|
|
return 'x64';
|
|
case 0x0c00:
|
|
return 'arm64';
|
|
default:
|
|
return undefined;
|
|
}
|
|
case 'windows':
|
|
// https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files#COFF_Header
|
|
switch ((exeBytes[0x7d] << 8) + exeBytes[0x7c]) {
|
|
case 0x014c:
|
|
return 'ia32';
|
|
case 0x8664:
|
|
return 'x64';
|
|
case 0xaa64:
|
|
return 'arm64';
|
|
default:
|
|
return undefined;
|
|
}
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function getExecutableInfo(
|
|
executablePath: string,
|
|
platform: string,
|
|
): ExecutableInfo | undefined {
|
|
if (!fileExists(executablePath)) {
|
|
return undefined;
|
|
}
|
|
|
|
const exeBytes = getExecutableBytes(executablePath);
|
|
return {
|
|
arch: getExecutableArch(exeBytes, platform),
|
|
};
|
|
}
|
|
|
|
export function getOptionsFromExecutable(
|
|
appResourcesDir: string,
|
|
priorOptions: NativefierOptions,
|
|
): NativefierOptions {
|
|
const newOptions: NativefierOptions = { ...priorOptions };
|
|
if (!newOptions.name) {
|
|
throw new Error(
|
|
'Can not extract options from executable with no name specified.',
|
|
);
|
|
}
|
|
const name: string = newOptions.name;
|
|
let executablePath: string | undefined = undefined;
|
|
|
|
const appRoot = path.resolve(path.join(appResourcesDir, '..', '..'));
|
|
const children = fs.readdirSync(appRoot, { withFileTypes: true });
|
|
const looksLikeMacOS =
|
|
children.filter((c) => c.name === 'MacOS' && c.isDirectory()).length > 0;
|
|
const looksLikeWindows =
|
|
children.filter((c) => c.name.toLowerCase().endsWith('.exe') && c.isFile())
|
|
.length > 0;
|
|
const looksLikeLinux =
|
|
children.filter((c) => c.name.toLowerCase().endsWith('.so') && c.isFile())
|
|
.length > 0;
|
|
|
|
if (looksLikeMacOS) {
|
|
log.debug('This looks like a MacOS app...');
|
|
if (newOptions.platform === undefined) {
|
|
newOptions.platform =
|
|
children.filter((c) => c.name === 'Library' && c.isDirectory()).length >
|
|
0
|
|
? 'mas'
|
|
: 'darwin';
|
|
}
|
|
executablePath = path.join(
|
|
appRoot,
|
|
'MacOS',
|
|
fs.readdirSync(path.join(appRoot, 'MacOS'))[0],
|
|
);
|
|
} else if (looksLikeWindows) {
|
|
log.debug('This looks like a Windows app...');
|
|
|
|
if (newOptions.platform === undefined) {
|
|
newOptions.platform = 'windows';
|
|
}
|
|
executablePath = path.join(
|
|
appRoot,
|
|
children.filter(
|
|
(c) =>
|
|
c.name.toLowerCase() === `${name.toLowerCase()}.exe` && c.isFile(),
|
|
)[0].name,
|
|
);
|
|
|
|
if (newOptions.appVersion === undefined) {
|
|
// https://github.com/electron/electron-packager/blob/f1c159f4c844d807968078ea504fba40ca7d9c73/src/win32.js#L46-L48
|
|
newOptions.appVersion = getVersionString(
|
|
executablePath,
|
|
'ProductVersion',
|
|
);
|
|
log.debug(
|
|
`Extracted app version from executable: ${
|
|
newOptions.appVersion as string
|
|
}`,
|
|
);
|
|
}
|
|
|
|
if (newOptions.buildVersion === undefined) {
|
|
//https://github.com/electron/electron-packager/blob/f1c159f4c844d807968078ea504fba40ca7d9c73/src/win32.js#L50-L52
|
|
newOptions.buildVersion = getVersionString(executablePath, 'FileVersion');
|
|
|
|
if (newOptions.appVersion == newOptions.buildVersion) {
|
|
newOptions.buildVersion = undefined;
|
|
} else {
|
|
log.debug(
|
|
`Extracted build version from executable: ${
|
|
newOptions.buildVersion as string
|
|
}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
if (newOptions.appCopyright === undefined) {
|
|
// https://github.com/electron/electron-packager/blob/f1c159f4c844d807968078ea504fba40ca7d9c73/src/win32.js#L54-L56
|
|
newOptions.appCopyright = getVersionString(
|
|
executablePath,
|
|
'LegalCopyright',
|
|
);
|
|
log.debug(
|
|
`Extracted app copyright from executable: ${
|
|
newOptions.appCopyright as string
|
|
}`,
|
|
);
|
|
}
|
|
} else if (looksLikeLinux) {
|
|
log.debug('This looks like a Linux app...');
|
|
if (newOptions.platform === undefined) {
|
|
newOptions.platform = 'linux';
|
|
}
|
|
executablePath = path.join(
|
|
appRoot,
|
|
children.filter((c) => c.name == name && c.isFile())[0].name,
|
|
);
|
|
}
|
|
|
|
if (!executablePath || !newOptions.platform) {
|
|
throw Error(
|
|
`Could not find executablePath or platform of app in ${appRoot}`,
|
|
);
|
|
}
|
|
|
|
log.debug(`Executable path: ${executablePath}`);
|
|
|
|
if (newOptions.arch === undefined) {
|
|
const executableInfo = getExecutableInfo(
|
|
executablePath,
|
|
newOptions.platform,
|
|
);
|
|
if (!executableInfo) {
|
|
throw new Error(
|
|
`Could not get executable info for executable path: ${executablePath}`,
|
|
);
|
|
}
|
|
|
|
newOptions.arch = executableInfo.arch;
|
|
log.debug(`Extracted arch from executable: ${newOptions.arch as string}`);
|
|
}
|
|
if (newOptions.platform === undefined || newOptions.arch == undefined) {
|
|
throw Error(`Could not determine platform / arch of app in ${appRoot}`);
|
|
}
|
|
|
|
return newOptions;
|
|
}
|