Skip to content

Instantly share code, notes, and snippets.

@thejustinwalsh
Created September 14, 2023 15:28
Show Gist options
  • Save thejustinwalsh/cf352e6186f5781dd6b9f2503ffaef60 to your computer and use it in GitHub Desktop.
Save thejustinwalsh/cf352e6186f5781dd6b9f2503ffaef60 to your computer and use it in GitHub Desktop.
nodejs unreal plugin build script (macOS)
import { readFile, readdir } from 'fs/promises'
import { existsSync } from 'fs';
import { resolve, join, sep } from 'path'
import { argv } from 'process';
import { spawn } from 'child_process';
const exec = (
cmd,
args,
) => new Promise((resolve, reject) => {
const cp = spawn(cmd, args, { stdio: ['ignore', 'inherit', 'inherit'] });
cp.on('exit', (code) => (code && code !== 0) ? reject(code) : resolve(0));
});
const getEngineVersion = async (uprojectPath) => {
const uproject = await readFile(uprojectPath, 'utf-8');
const project = JSON.parse(uproject);
const engineVersion = project.EngineAssociation;
if (!engineVersion) throw new Error(`Could not find engine version in ${uprojectPath}`);
return engineVersion;
}
const getInstallLocation = async (engineVersion) => {
const installIniPath = join(process.env.HOME, 'Library/Application Support/Epic/UnrealEngine/Install.ini');
const installIni = await readFile(installIniPath, 'utf-8');
const matches = installIni.match(new RegExp(`^{${engineVersion}}=(.*)`, 'gm'));
let installLocation = matches ? matches[1] : "";
if (!installLocation) {
const dirListing = await readdir(join(process.env.HOME, 'Library/Application Support/Epic/UnrealEngine'));
const configCacheFiles = dirListing.filter(file => file.startsWith('XmlConfigCache'));
const configCache = configCacheFiles.find(file => file.includes(`UE_${engineVersion}`));
if (!configCache) throw new Error(`Could not find Unreal Engine ${engineVersion} installation location`);
const encodedPath = configCache.match(/XmlConfigCache-(.*)\.bin/)[1] ?? "";
if (encodedPath) installLocation = encodedPath.replace(/\+/g, sep);
}
if (!installLocation) throw new Error(`Could not find Unreal Engine ${engineVersion} installation location`);
return installLocation;
}
const getRunUATPath = async (engineVersion) => {
const installLocation = await getInstallLocation(engineVersion);
const runUATPath = join(installLocation, `Engine/Build/BatchFiles/RunUAT.${process.platform === 'win32' ? 'bat' : 'sh'}`);
if (!existsSync(runUATPath)) throw new Error(`Could not find RunUAT script in ${installLocation}`);
return runUATPath;
}
const getPluginPath = async (uprojectPath, pluginName) => {
const uprojectDir = resolve(uprojectPath, '..');
const pluginDir = join(uprojectDir, 'Plugins', pluginName);
const pluginFile = join(pluginDir, `${pluginName}.uplugin`);
if (!existsSync(pluginFile)) throw new Error(`Could not find plugin ${pluginName} in ${pluginDir}`);
return pluginFile;
}
const buildPlugin = async (engineVersion, pluginPath) => {
const runUATPath = await getRunUATPath(engineVersion);
const exitCode = await exec(runUATPath, ["BuildPlugin", `-Plugin=${pluginPath}`, `-Package=${process.cwd()}/Packages/Mac`, "-Rocket", "-TargetPlatforms=Mac"]);
if (exitCode !== 0) throw new Error(`Failed to build plugin ${pluginPath}`);
}
const main = async () => {
if (argv.length < 4) throw new Error('Usage: build.mjs <uproject path> <plugin name>');
if (process.platform !== 'darwin') throw new Error('TODO: Need to modify this script to work on windows and linux');
const uprojectPath = resolve(argv[2]);
const plugin = await getPluginPath(uprojectPath, argv[3]);
const engineVersion = await getEngineVersion(uprojectPath);
await buildPlugin(engineVersion, plugin);
}
await main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment