mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
arm64 support
This commit is contained in:
@@ -13,8 +13,10 @@
|
||||
"test:coverage": "jest --coverage",
|
||||
"prod": "cross-env PLATFORM=windows webpack --config webpack.config.js --mode production",
|
||||
"prod-linux": "cross-env PLATFORM=linux webpack --config webpack.config.js --mode production",
|
||||
"build-electron": "node ./src-electron/download-dotnet-runtime.js && node ./src-electron/patch-package-version.js && electron-builder --publish never",
|
||||
"postbuild-electron": "node ./src-electron/patch-node-api-dotnet.js && node ./src-electron/rename-builds.js",
|
||||
"build-electron": "node ./src-electron/download-dotnet-runtime.js --arch=x64 && node ./src-electron/patch-package-version.js && electron-builder --x64 --publish never",
|
||||
"build-electron-arm64": "node ./src-electron/download-dotnet-runtime.js --arch=arm64 && node ./src-electron/patch-package-version.js && electron-builder --arm64 --publish never",
|
||||
"postbuild-electron": "node ./src-electron/patch-node-api-dotnet.js --arch=x64 && node ./src-electron/rename-builds.js --arch=x64",
|
||||
"postbuild-electron-arm64": "node ./src-electron/patch-node-api-dotnet.js --arch=arm64 && node ./src-electron/rename-builds.js --arch=arm64",
|
||||
"start-electron": "electron ."
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -2,30 +2,9 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
let runnerArch = process.arch.toString();
|
||||
let runnerPlatform = process.platform.toString();
|
||||
const args = process.argv.slice(2);
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--arch' && i + 1 < args.length) {
|
||||
runnerArch = args[i + 1];
|
||||
} else if (args[i] === '--platform' && i + 1 < args.length) {
|
||||
runnerPlatform = args[i + 1];
|
||||
}
|
||||
}
|
||||
let platform = '';
|
||||
if (runnerPlatform === 'linux') {
|
||||
platform = 'linux';
|
||||
} else if (runnerPlatform === 'darwin') {
|
||||
platform = 'osx';
|
||||
} else if (runnerPlatform === 'win32') {
|
||||
platform = 'win';
|
||||
} else {
|
||||
throw new Error(`Unsupported platform: ${runnerPlatform}`);
|
||||
}
|
||||
const { getArchAndPlatform } = require('./utils');
|
||||
|
||||
const DOTNET_VERSION = '9.0.8';
|
||||
const DOTNET_RUNTIME_URL = `https://builds.dotnet.microsoft.com/dotnet/Runtime/${DOTNET_VERSION}/dotnet-runtime-${DOTNET_VERSION}-${platform}-${runnerArch}.tar.gz`;
|
||||
const DOTNET_RUNTIME_DIR = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
@@ -78,78 +57,80 @@ async function extractTarGz(tarGzPath, extractDir) {
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
if (platform !== 'linux' && platform !== 'darwin') {
|
||||
console.log('Skipping .NET runtime download on non supported platform');
|
||||
return;
|
||||
async function downloadDotnetRuntime(arch, platform) {
|
||||
if (!arch || !platform) {
|
||||
throw new Error('Architecture and platform must be specified');
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Downloading .NET ${DOTNET_VERSION}-${platform}-${runnerArch} runtime...`
|
||||
);
|
||||
let dotnetPlatform = '';
|
||||
if (platform === 'linux') {
|
||||
dotnetPlatform = 'linux';
|
||||
} else if (platform === 'darwin') {
|
||||
dotnetPlatform = 'osx';
|
||||
} else if (platform === 'win32') {
|
||||
dotnetPlatform = 'win';
|
||||
} else {
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(DOTNET_RUNTIME_DIR)) {
|
||||
fs.mkdirSync(DOTNET_RUNTIME_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Downloading .NET ${DOTNET_VERSION}-${dotnetPlatform}-${arch} runtime...`
|
||||
);
|
||||
const tarGzPath = path.join(DOTNET_RUNTIME_DIR, 'dotnet-runtime.tar.gz');
|
||||
const dotnetRuntimeUrl = `https://builds.dotnet.microsoft.com/dotnet/Runtime/${DOTNET_VERSION}/dotnet-runtime-${DOTNET_VERSION}-${dotnetPlatform}-${arch}.tar.gz`;
|
||||
|
||||
try {
|
||||
// Download .NET runtime
|
||||
await downloadFile(DOTNET_RUNTIME_URL, tarGzPath);
|
||||
console.log('Download completed');
|
||||
// Download .NET runtime
|
||||
await downloadFile(dotnetRuntimeUrl, tarGzPath);
|
||||
console.log('Download completed');
|
||||
|
||||
// Extract .NET runtime to a temporary directory first
|
||||
const tempExtractDir = path.join(DOTNET_RUNTIME_DIR, 'temp');
|
||||
if (!fs.existsSync(tempExtractDir)) {
|
||||
fs.mkdirSync(tempExtractDir, { recursive: true });
|
||||
}
|
||||
|
||||
console.log('Extracting .NET runtime...');
|
||||
await extractTarGz(tarGzPath, tempExtractDir);
|
||||
console.log('Extraction completed');
|
||||
|
||||
// Clean up tar.gz file
|
||||
fs.unlinkSync(tarGzPath);
|
||||
console.log('Cleanup completed');
|
||||
|
||||
// Ensure the dotnet executable is executable
|
||||
const extractedDotnet = path.join(tempExtractDir, 'dotnet');
|
||||
fs.chmodSync(extractedDotnet, 0o755);
|
||||
|
||||
// Move all other files to the root of dotnet-runtime
|
||||
const files = fs.readdirSync(tempExtractDir);
|
||||
for (const file of files) {
|
||||
const sourcePath = path.join(tempExtractDir, file);
|
||||
const targetPath = path.join(DOTNET_RUNTIME_DIR, file);
|
||||
|
||||
if (fs.existsSync(targetPath)) {
|
||||
if (fs.lstatSync(sourcePath).isDirectory()) {
|
||||
// Remove existing directory and move new one
|
||||
fs.rmSync(targetPath, { recursive: true, force: true });
|
||||
} else {
|
||||
// Remove existing file
|
||||
fs.unlinkSync(targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
fs.renameSync(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
// Clean up temp directory
|
||||
fs.rmSync(tempExtractDir, { recursive: true, force: true });
|
||||
|
||||
console.log(
|
||||
`.NET runtime downloaded and extracted to: ${DOTNET_RUNTIME_DIR}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
process.exit(1);
|
||||
// Extract .NET runtime to a temporary directory first
|
||||
const tempExtractDir = path.join(DOTNET_RUNTIME_DIR, 'temp');
|
||||
if (!fs.existsSync(tempExtractDir)) {
|
||||
fs.mkdirSync(tempExtractDir, { recursive: true });
|
||||
}
|
||||
|
||||
console.log('Extracting .NET runtime...');
|
||||
await extractTarGz(tarGzPath, tempExtractDir);
|
||||
console.log('Extraction completed');
|
||||
|
||||
// Clean up tar.gz file
|
||||
fs.unlinkSync(tarGzPath);
|
||||
console.log('Cleanup completed');
|
||||
|
||||
// Ensure the dotnet executable is executable
|
||||
const extractedDotnet = path.join(tempExtractDir, 'dotnet');
|
||||
fs.chmodSync(extractedDotnet, 0o755);
|
||||
|
||||
// Move all other files to the root of dotnet-runtime
|
||||
const files = fs.readdirSync(tempExtractDir);
|
||||
for (const file of files) {
|
||||
const sourcePath = path.join(tempExtractDir, file);
|
||||
const targetPath = path.join(DOTNET_RUNTIME_DIR, file);
|
||||
|
||||
if (fs.existsSync(targetPath)) {
|
||||
if (fs.lstatSync(sourcePath).isDirectory()) {
|
||||
// Remove existing directory and move new one
|
||||
fs.rmSync(targetPath, { recursive: true, force: true });
|
||||
} else {
|
||||
// Remove existing file
|
||||
fs.unlinkSync(targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
fs.renameSync(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
// Clean up temp directory
|
||||
fs.rmSync(tempExtractDir, { recursive: true, force: true });
|
||||
|
||||
console.log(
|
||||
`.NET runtime downloaded and extracted to: ${DOTNET_RUNTIME_DIR}`
|
||||
);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { downloadFile, extractTarGz };
|
||||
const { arch, platform } = getArchAndPlatform();
|
||||
downloadDotnetRuntime(arch, platform);
|
||||
|
||||
@@ -16,16 +16,11 @@ const https = require('https');
|
||||
|
||||
//app.disableHardwareAcceleration();
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
const bundledDotNetPath = path.join(process.resourcesPath, 'dotnet-runtime');
|
||||
if (fs.existsSync(bundledDotNetPath)) {
|
||||
// Include bundled .NET runtime
|
||||
const bundledDotNetPath = path.join(
|
||||
process.resourcesPath,
|
||||
'dotnet-runtime'
|
||||
);
|
||||
if (fs.existsSync(bundledDotNetPath)) {
|
||||
process.env.DOTNET_ROOT = bundledDotNetPath;
|
||||
process.env.PATH = `${bundledDotNetPath}:${process.env.PATH}`;
|
||||
}
|
||||
process.env.DOTNET_ROOT = bundledDotNetPath;
|
||||
process.env.PATH = `${bundledDotNetPath}:${process.env.PATH}`;
|
||||
} else if (process.platform === 'darwin') {
|
||||
const dotnetPath = path.join('/usr/local/share/dotnet');
|
||||
const dotnetPathArm = path.join('/usr/local/share/dotnet/x64');
|
||||
@@ -154,6 +149,10 @@ if (!gotTheLock) {
|
||||
});
|
||||
}
|
||||
|
||||
ipcMain.handle('getArch', () => {
|
||||
return process.arch.toString();
|
||||
});
|
||||
|
||||
ipcMain.handle('applyWindowSettings', (event, position, size, state) => {
|
||||
if (position) {
|
||||
mainWindow.setPosition(parseInt(position.x), parseInt(position.y));
|
||||
@@ -812,6 +811,10 @@ function isDotNetInstalled() {
|
||||
const result = spawnSync(dotnetPath, ['--list-runtimes'], {
|
||||
encoding: 'utf-8'
|
||||
});
|
||||
if (result.error) {
|
||||
console.error('Error checking .NET runtimes:', result.error);
|
||||
return false;
|
||||
}
|
||||
return result.stdout?.includes('.NETCore.App 9.0');
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { getArchAndPlatform } = require('./utils');
|
||||
|
||||
function patchFile(filePath) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
@@ -30,25 +31,30 @@ managedHostPath = managedHostPath.indexOf('app.asar.unpacked') < 0 ?
|
||||
return false;
|
||||
}
|
||||
|
||||
// Paths to patch
|
||||
let platformName = '';
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
platformName = 'win';
|
||||
break;
|
||||
case 'darwin':
|
||||
platformName = 'mac';
|
||||
break;
|
||||
case 'linux':
|
||||
platformName = 'linux';
|
||||
break;
|
||||
function patchNodeApiDotNet(arch, platform) {
|
||||
let platformName = '';
|
||||
switch (platform) {
|
||||
case 'win32':
|
||||
platformName = 'win';
|
||||
break;
|
||||
case 'darwin':
|
||||
platformName = 'mac';
|
||||
break;
|
||||
case 'linux':
|
||||
platformName = 'linux';
|
||||
break;
|
||||
}
|
||||
if (arch === 'arm64') {
|
||||
platformName += '-arm64';
|
||||
}
|
||||
|
||||
const postBuildPath = path.join(
|
||||
__dirname,
|
||||
`./../build/${platformName}-unpacked/resources/app.asar.unpacked/node_modules/node-api-dotnet/init.js`
|
||||
);
|
||||
console.log('Patching post-build init.js...');
|
||||
patchFile(postBuildPath);
|
||||
}
|
||||
if (process.arch === 'arm64') {
|
||||
platformName += '-arm64';
|
||||
}
|
||||
const postBuildPath = path.join(
|
||||
__dirname,
|
||||
`./../build/${platformName}-unpacked/resources/app.asar.unpacked/node_modules/node-api-dotnet/init.js`
|
||||
);
|
||||
console.log('Patching post-build init.js...');
|
||||
patchFile(postBuildPath);
|
||||
|
||||
const { arch, platform } = getArchAndPlatform();
|
||||
patchNodeApiDotNet(arch, platform);
|
||||
|
||||
@@ -22,6 +22,7 @@ contextBridge.exposeInMainWorld('interopApi', {
|
||||
const validChannels = ['launch-command'];
|
||||
|
||||
contextBridge.exposeInMainWorld('electron', {
|
||||
getArch: () => ipcRenderer.invoke('getArch'),
|
||||
openFileDialog: () => ipcRenderer.invoke('dialog:openFile'),
|
||||
openDirectoryDialog: () => ipcRenderer.invoke('dialog:openDirectory'),
|
||||
onWindowPositionChanged: (callback) =>
|
||||
@@ -48,7 +49,6 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
ipcRenderer: {
|
||||
on(channel, func) {
|
||||
if (validChannels.includes(channel)) {
|
||||
console.log('contextBridge', channel, func);
|
||||
ipcRenderer.on(channel, (event, ...args) => func(...args));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { getArchAndPlatform } = require('./utils');
|
||||
|
||||
const rootDir = path.join(__dirname, '..');
|
||||
const versionFilePath = path.join(rootDir, 'Version');
|
||||
@@ -17,34 +18,42 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
const oldAppImage = path.join(buildDir, `VRCX_Version.AppImage`);
|
||||
const newAppImage = path.join(buildDir, `VRCX_${version}.AppImage`);
|
||||
try {
|
||||
if (fs.existsSync(oldAppImage)) {
|
||||
fs.renameSync(oldAppImage, newAppImage);
|
||||
console.log(`Renamed: ${oldAppImage} -> ${newAppImage}`);
|
||||
} else {
|
||||
console.log(`File not found: ${oldAppImage}`);
|
||||
function renameBuild(arch, platform) {
|
||||
if (platform === 'linux') {
|
||||
const oldAppImage = path.join(buildDir, `VRCX_Version.AppImage`);
|
||||
const newAppImage = path.join(
|
||||
buildDir,
|
||||
`VRCX_${version}_${arch}.AppImage`
|
||||
);
|
||||
try {
|
||||
if (fs.existsSync(oldAppImage)) {
|
||||
fs.renameSync(oldAppImage, newAppImage);
|
||||
console.log(`Renamed: ${oldAppImage} -> ${newAppImage}`);
|
||||
} else {
|
||||
console.log(`File not found: ${oldAppImage}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error renaming files:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error renaming files:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
const oldDmg = path.join(buildDir, `VRCX_Version.dmg`);
|
||||
const newDmg = path.join(buildDir, `VRCX_${version}.dmg`);
|
||||
try {
|
||||
if (fs.existsSync(oldDmg)) {
|
||||
fs.renameSync(oldDmg, newDmg);
|
||||
console.log(`Renamed: ${oldDmg} -> ${newDmg}`);
|
||||
} else {
|
||||
console.log(`File not found: ${oldDmg}`);
|
||||
} else if (platform === 'darwin') {
|
||||
const oldDmg = path.join(buildDir, `VRCX_Version.dmg`);
|
||||
const newDmg = path.join(buildDir, `VRCX_${version}_${arch}.dmg`);
|
||||
try {
|
||||
if (fs.existsSync(oldDmg)) {
|
||||
fs.renameSync(oldDmg, newDmg);
|
||||
console.log(`Renamed: ${oldDmg} -> ${newDmg}`);
|
||||
} else {
|
||||
console.log(`File not found: ${oldDmg}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error renaming files:', err);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error renaming files:', err);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('No renaming needed for this platform.');
|
||||
}
|
||||
} else {
|
||||
console.log('No renaming needed for this platform.');
|
||||
}
|
||||
|
||||
const { arch, platform } = getArchAndPlatform();
|
||||
renameBuild(arch, platform);
|
||||
|
||||
19
src-electron/utils.js
Normal file
19
src-electron/utils.js
Normal file
@@ -0,0 +1,19 @@
|
||||
function getArchAndPlatform() {
|
||||
// --arch= win32, darwin, linux
|
||||
// --platform= x64, arm64
|
||||
const args = process.argv.slice(2);
|
||||
let arch = process.arch.toString();
|
||||
let platform = process.platform.toString();
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i].startsWith('--arch=')) {
|
||||
arch = args[i].split('=')[1];
|
||||
}
|
||||
if (args[i].startsWith('--platform=')) {
|
||||
platform = args[i].split('=')[1];
|
||||
}
|
||||
}
|
||||
console.log(`Using arch: ${arch}, platform: ${platform}`);
|
||||
return { arch, platform };
|
||||
}
|
||||
|
||||
module.exports = { getArchAndPlatform };
|
||||
@@ -14,6 +14,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
||||
const { t } = useI18n();
|
||||
|
||||
const state = reactive({
|
||||
arch: 'x64',
|
||||
appVersion: '',
|
||||
autoUpdateVRCX: 'Auto Download',
|
||||
latestAppVersion: '',
|
||||
@@ -40,6 +41,12 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
||||
});
|
||||
|
||||
async function initVRCXUpdaterSettings() {
|
||||
if (!WINDOWS) {
|
||||
const arch = await window.electron.getArch();
|
||||
console.log('Architecture:', arch);
|
||||
state.arch = arch;
|
||||
}
|
||||
|
||||
const [autoUpdateVRCX, vrcxId] = await Promise.all([
|
||||
configRepository.getString('VRCX_autoUpdateVRCX', 'Auto Download'),
|
||||
configRepository.getString('VRCX_id', '')
|
||||
@@ -200,7 +207,7 @@ export const useVRCXUpdaterStore = defineStore('VRCXUpdater', () => {
|
||||
}
|
||||
if (
|
||||
LINUX &&
|
||||
asset.name.endsWith('.AppImage') &&
|
||||
asset.name.endsWith(`${state.arch}.AppImage`) &&
|
||||
asset.content_type === 'application/octet-stream'
|
||||
) {
|
||||
downloadUrl = asset.browser_download_url;
|
||||
|
||||
3
src/types/globals.d.ts
vendored
3
src/types/globals.d.ts
vendored
@@ -30,6 +30,7 @@ declare global {
|
||||
) => Promise<any>;
|
||||
};
|
||||
electron: {
|
||||
getArch: () => Promise<string>;
|
||||
openFileDialog: () => Promise<string>;
|
||||
openDirectoryDialog: () => Promise<string>;
|
||||
desktopNotification: (
|
||||
@@ -63,7 +64,7 @@ declare global {
|
||||
overlayHand: int
|
||||
) => Promise<void>;
|
||||
ipcRenderer: {
|
||||
on(channel: String, func: (...args: unknown[]) => void)
|
||||
on(channel: String, func: (...args: unknown[]) => void);
|
||||
};
|
||||
};
|
||||
__APP_GLOBALS__: AppGlobals;
|
||||
|
||||
Reference in New Issue
Block a user