Update Build Script

This commit is contained in:
TheBrokenRail 2025-01-03 08:25:09 -05:00
parent c5f9a519c5
commit b767b9d4ec
7 changed files with 205 additions and 162 deletions

View File

@ -9,7 +9,7 @@ elseif(CPACK_MCPI_ARCH STREQUAL "amd64")
endif() endif()
set(RUNTIME "${CPACK_TOPLEVEL_DIRECTORY}/runtime") set(RUNTIME "${CPACK_TOPLEVEL_DIRECTORY}/runtime")
file(DOWNLOAD file(DOWNLOAD
"https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-${RUNTIME_ARCH}" "https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-${RUNTIME_ARCH}"
"${RUNTIME}" "${RUNTIME}"
STATUS DOWNLOAD_STATUS STATUS DOWNLOAD_STATUS
) )
@ -34,10 +34,11 @@ execute_process(
COMMAND COMMAND
"${CMAKE_COMMAND}" "-E" "env" "${CMAKE_COMMAND}" "-E" "env"
"ARCH=${APPIMAGE_ARCH}" "ARCH=${APPIMAGE_ARCH}"
"VERSION=${CPACK_MCPI_VERSION}"
"appimagetool" "appimagetool"
"--updateinformation" "zsync|${CPACK_MCPI_REPO}/releases/download/latest/${CPACK_PACKAGE_FILE_NAME_ZSYNC}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}" "--updateinformation" "zsync|${CPACK_MCPI_REPO}/releases/download/latest/${CPACK_PACKAGE_FILE_NAME_ZSYNC}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}"
"--runtime-file" "${RUNTIME}" "--runtime-file" "${RUNTIME}"
"--comp" "xz" "--comp" "zstd"
"${CPACK_TEMPORARY_DIRECTORY}" "${CPACK_TEMPORARY_DIRECTORY}"
"${CPACK_PACKAGE_FILE_NAME}${CPACK_MCPI_APPIMAGE_EXT}" "${CPACK_PACKAGE_FILE_NAME}${CPACK_MCPI_APPIMAGE_EXT}"
WORKING_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}" WORKING_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}"

View File

@ -21,6 +21,7 @@ if(MCPI_IS_APPIMAGE_BUILD)
macro(pass_to_cpack var) macro(pass_to_cpack var)
set("CPACK_MCPI_${var}" "${MCPI_${var}}") set("CPACK_MCPI_${var}" "${MCPI_${var}}")
endmacro() endmacro()
pass_to_cpack(VERSION)
pass_to_cpack(ARCH) pass_to_cpack(ARCH)
pass_to_cpack(REPO) pass_to_cpack(REPO)
pass_to_cpack(APPIMAGE_EXT) pass_to_cpack(APPIMAGE_EXT)

View File

@ -27,7 +27,7 @@ The AppImage requires Debian Bullseye or higher. This is equivalent to Ubuntu 20
It also requires some additional packages. To install them, run: It also requires some additional packages. To install them, run:
```sh ```sh
sudo apt install -y libfuse2 libopenal1 libglib2.0-0 sudo apt install -y libopenal1 libglib2.0-0
``` ```
</details> </details>

View File

@ -2,113 +2,28 @@
import * as path from 'node:path'; import * as path from 'node:path';
import * as url from 'node:url'; import * as url from 'node:url';
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import * as child_process from 'node:child_process'; import { info, err, run } from './lib/util.mjs';
import { parseOptions, Enum, Architectures } from './lib/options.mjs';
// Logging
const EXIT_FAILURE = 1;
function fail(message) {
console.error(message);
process.exit(EXIT_FAILURE);
}
function err(message) {
fail('ERROR: ' + message);
}
function info(message) {
console.log('INFO: ' + message);
}
// Enums // Enums
function Enum(values) { const PackageTypes = new Enum([
for (const value of values) {
this[value] = {
prettyName: value,
name: value.toLowerCase()
};
}
}
Enum.prototype.get = function (name) {
for (const value in this) {
if (value.toLowerCase() === name.toLowerCase()) {
return this[value];
}
}
return null;
};
function wrap(obj) {
return new Proxy(obj, {
get(target, property) {
if (property in target) {
return target[property];
} else {
err('Undefined Value: ' + property);
}
}
});
}
const PackageTypes = wrap(new Enum([
'None', 'None',
'AppImage', 'AppImage',
'Flatpak' 'Flatpak'
])); ]);
const Architectures = wrap(new Enum([
'AMD64',
'ARM64',
'ARMHF',
'Host'
]));
const Configurations = wrap(new Enum([
'Release',
'Debug'
]));
// Folders // CMake Options
const __filename = url.fileURLToPath(import.meta.url); const cmakeArgPrefix = '-D';
const __dirname = path.dirname(__filename); const cmakeArgSeparator = '=';
const root = path.join(__dirname, '..'); const cmakeOptions = new Map();
let build = path.join(root, 'build'); function parseCMakeOption(arg) {
let out = path.join(root, 'out'); if (arg === null) {
// Usage Text
// Positional Arguments return `${cmakeArgPrefix}var${cmakeArgSeparator}value...`;
let argIndex = 2; // Skip First Two Arguments } else if (arg.startsWith(cmakeArgPrefix)) {
function parseArg(arg, from, type) {
// Check Argument
if (arg === undefined) {
err('Expecting ' + type);
}
// Read Argument
const value = from.get(arg);
if (value === null) {
err(`Invalid ${type}: ${arg}`);
}
// Return
return value;
}
function readArg(...args) {
return parseArg(process.argv[argIndex++], ...args);
}
// Type Of Packaging
const packageType = readArg(PackageTypes, 'Package Type');
// Build Architecture
const architecture = readArg(Architectures, 'Architecture');
// Flatpak Builds Work Best Without Custom Toolchains
if (packageType === PackageTypes.Flatpak && architecture !== Architectures.Host) {
err('Flatpak Builds Do Not Support Custom Toolchains');
}
// CMake Build Options
const options = new Map();
// Other Arguments
let clean = false;
let install = false;
let config = Configurations.Release;
for (; argIndex < process.argv.length; argIndex++) {
const arg = process.argv[argIndex];
const cmakeArgPrefix = '-D';
if (arg.startsWith(cmakeArgPrefix)) {
// Pass Build Option To CMake // Pass Build Option To CMake
let parsedArg = arg.substring(cmakeArgPrefix.length); let parsedArg = arg.substring(cmakeArgPrefix.length);
const parts = parsedArg.split('='); const parts = parsedArg.split(cmakeArgSeparator);
if (parts.length !== 2) { if (parts.length !== 2) {
err('Unable To Parse Build Option: ' + arg); err('Unable To Parse Build Option: ' + arg);
} }
@ -117,50 +32,68 @@ for (; argIndex < process.argv.length; argIndex++) {
if (!/^[a-zA-Z_]+$/.test(name) || name.length === 0) { if (!/^[a-zA-Z_]+$/.test(name) || name.length === 0) {
err('Invalid Build Option Name: ' + name); err('Invalid Build Option Name: ' + name);
} }
options.set(name, value); cmakeOptions.set(name, value);
} else if (arg === '--clean') { return true;
// Remove Existing Build Directory
clean = true;
} else if (arg === '--install') {
// Install To System Instead Of Output Directory
if (packageType === PackageTypes.AppImage) {
err('AppImages Cannot Be Installed');
}
install = true;
} else if (arg === '--config') {
// Set Configuration
config = parseArg(process.argv[++argIndex], Configurations, 'Configuration');
} else { } else {
// Invalid // Unknown Option
err('Invalid Argument: ' + arg); return false;
} }
} }
// Update Folders // Options
function updateDir(dir) { const options = parseOptions([
if (packageType !== PackageTypes.None) { ['packageType', PackageTypes],
dir = path.join(dir, packageType.name); ['architecture', Architectures]
} ], [
return path.join(dir, architecture.name); 'clean',
'install',
'debug'
], parseCMakeOption);
// Check Options
if (options.packageType === PackageTypes.Flatpak && options.architecture !== Architectures.Host) {
err('Flatpak Builds Do Not Support Custom Toolchains');
} }
build = updateDir(build); if (options.packageType === PackageTypes.AppImage && options.install) {
let cleanOut = false; err('AppImages Cannot Be Installed');
// AppImages Are Placed Directly In ./out
if (packageType !== PackageTypes.AppImage) {
cleanOut = true;
out = updateDir(out);
} }
// Configure Build Options // Folders
function toCmakeBool(val) { const __filename = url.fileURLToPath(import.meta.url);
return val ? 'ON' : 'OFF'; const __dirname = path.dirname(__filename);
const root = path.join(__dirname, '..');
let build = path.join(root, 'build');
let out = path.join(root, 'out');
// Update Build Directory
function specializeDir(dir) {
// Use Unique Folder For Build Type
return path.join(dir, options.packageType.name, options.architecture.name);
} }
options.set('MCPI_IS_APPIMAGE_BUILD', toCmakeBool(packageType === PackageTypes.AppImage)); build = specializeDir(build);
options.set('MCPI_IS_FLATPAK_BUILD', toCmakeBool(packageType === PackageTypes.Flatpak)); // Update Output Directory
if (architecture !== Architectures.Host) { const useOutRoot = options.packageType === PackageTypes.AppImage;
options.set('CMAKE_TOOLCHAIN_FILE', path.join(root, 'cmake', 'toolchain', architecture.name + '-toolchain.cmake')); if (!useOutRoot) {
out = specializeDir(out);
}
// Print Directories
function printDir(name, dir) {
info(name + ' Directory: ' + dir);
}
printDir('Build', build);
printDir('Output', out);
// Configure CMake Options
function setupPackageTypeOption(type) {
cmakeOptions.set(`MCPI_IS_${type.name.toUpperCase()}_BUILD`, options.packageType === type ? 'ON' : 'OFF');
}
setupPackageTypeOption(PackageTypes.AppImage);
setupPackageTypeOption(PackageTypes.Flatpak);
const toolchainOption = 'CMAKE_TOOLCHAIN_FILE';
if (options.architecture !== Architectures.Host) {
cmakeOptions.set(toolchainOption, path.join(root, 'cmake', 'toolchain', options.architecture.name + '-toolchain.cmake'));
} else { } else {
options.delete('CMAKE_TOOLCHAIN_FILE'); cmakeOptions.delete(toolchainOption);
} }
// Make Build Directory // Make Build Directory
@ -170,39 +103,31 @@ function createDir(dir, clean) {
} }
fs.mkdirSync(dir, {recursive: true}); fs.mkdirSync(dir, {recursive: true});
} }
createDir(build, clean); createDir(build, options.clean);
if (!install) { if (!options.install) {
createDir(out, cleanOut); createDir(out, !useOutRoot);
} }
// Run CMake // Run CMake
function run(command) {
try {
info('Running: ' + command.join(' '));
child_process.execFileSync(command[0], command.slice(1), {cwd: build, stdio: 'inherit'});
} catch (e) {
err(e);
}
}
const configure = ['cmake', '-GNinja Multi-Config']; const configure = ['cmake', '-GNinja Multi-Config'];
options.forEach((value, key, map) => { cmakeOptions.forEach((value, key) => {
configure.push(`-D${key}=${value}`); configure.push(cmakeArgPrefix + key + cmakeArgSeparator + value);
}); });
configure.push(root); configure.push('-S', root, '-B', build);
run(configure); run(configure);
// Build // Build
const configArg = ['--config', config.prettyName]; const configArg = ['--config', options.debug ? 'Debug' : 'Release'];
run(['cmake', '--build', '.', ...configArg/*, '-v'/*, '--', '-n', '-j1', '-j1'*/]); run(['cmake', '--build', build, ...configArg]);
// Package // Package
if (packageType !== PackageTypes.AppImage) { if (options.packageType !== PackageTypes.AppImage) {
if (!install) { if (!options.install) {
process.env.DESTDIR = out; process.env.DESTDIR = out;
} }
run(['cmake', '--install', '.', ...configArg]); run(['cmake', '--install', build, ...configArg]);
} else { } else {
run(['cmake', '--build', '.', '--target', 'package', ...configArg]); run(['cmake', '--build', build, '--target', 'package', ...configArg]);
// Copy Generated Files // Copy Generated Files
const files = fs.readdirSync(build); const files = fs.readdirSync(build);
for (const file of files) { for (const file of files) {

View File

@ -61,19 +61,19 @@ run_build() {
"libxext-dev:$1" \ "libxext-dev:$1" \
`# QEMU Dependencies` \ `# QEMU Dependencies` \
"libglib2.0-dev:$1" \ "libglib2.0-dev:$1" \
`# AppStream Verification` \ `# AppImage` \
appstream appstream \
zsync
# Install appimagetool # Install appimagetool
sudo rm -rf /opt/squashfs-root /opt/appimagetool /usr/local/bin/appimagetool sudo rm -rf /opt/squashfs-root /opt/appimagetool /usr/local/bin/appimagetool
case "$(dpkg --print-architecture)" in case "$(dpkg --print-architecture)" in
'armhf') APPIMAGE_ARCH='armhf';; 'armhf') APPIMAGE_ARCH='armhf';;
'arm64') APPIMAGE_ARCH='aarch64';; 'arm64') APPIMAGE_ARCH='aarch64';;
'i386') APPIMAGE_ARCH='i686';;
'amd64') APPIMAGE_ARCH='x86_64';; 'amd64') APPIMAGE_ARCH='x86_64';;
esac esac
sudo mkdir -p /opt sudo mkdir -p /opt
sudo wget -O /opt/appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${APPIMAGE_ARCH}.AppImage" sudo wget -O /opt/appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${APPIMAGE_ARCH}.AppImage"
sudo chmod +x /opt/appimagetool sudo chmod +x /opt/appimagetool
# Workaround AppImage Issues With Docker # Workaround AppImage Issues With Docker
sudo ./scripts/fix-appimage-for-docker.sh /opt/appimagetool sudo ./scripts/fix-appimage-for-docker.sh /opt/appimagetool
@ -83,7 +83,8 @@ run_build() {
sudo rm -f ./appimagetool sudo rm -f ./appimagetool
# Link # Link
sudo mv ./squashfs-root ./appimagetool sudo mv ./squashfs-root ./appimagetool
sudo ln -s /opt/appimagetool/AppRun /usr/local/bin/appimagetool printf '#!/bin/sh\nexec /opt/appimagetool/AppRun "$@"\n' | sudo tee /usr/local/bin/appimagetool > /dev/null
sudo chmod +x /usr/local/bin/appimagetool
} }
# Test Dependencies # Test Dependencies

91
scripts/lib/options.mjs Normal file
View File

@ -0,0 +1,91 @@
import { fail } from './util.mjs';
// Enums
export function Enum(values) {
this.values = [];
for (const value of values) {
const obj = {
prettyName: value,
name: value.toLowerCase()
};
this[value] = obj;
this.values.push(obj);
}
}
Enum.prototype.get = function (name) {
if (name) {
for (const value of this.values) {
if (value.name === name.toLowerCase()) {
return value;
}
}
}
return null;
};
// Supported Architectures
export const Architectures = new Enum([
'AMD64',
'ARM64',
'ARMHF',
'Host'
]);
// Parse
function formatFlag(name) {
return '--' + name;
}
function formatOptionalArg(arg) {
return '[' + arg + '] ';;
}
export function parseOptions(positionalArgs, flags, customHandler) {
// Usage Text
let usage = 'USAGE: ';
for (const arg of positionalArgs) {
const option = arg[1];
const arr = [];
for (const value of option.values) {
arr.push(value.name);
}
usage += '<' + arr.join('|') + '> ';
}
for (const flag of flags) {
usage += formatOptionalArg(formatFlag(flag));
}
usage += formatOptionalArg(customHandler(null));
usage = usage.trim();
// Copy Arguments
const args = process.argv.slice(2); // Skip First Two Arguments
// Read Positional Arguments
const out = {};
for (const arg of positionalArgs) {
let value = args.shift();
value = arg[1].get(value);
if (value === null) {
fail(usage);
}
out[arg[0]] = value;
}
// Read Flags
for (const flag of flags) {
const name = formatFlag(flag);
const isSet = args.includes(name);
if (isSet) {
args.splice(args.indexOf(name), 1);
}
out[flag] = isSet;
}
// Unknown Arguments
for (const arg of args) {
if (!customHandler(arg)) {
fail(usage);
}
}
// Return
return out;
}

24
scripts/lib/util.mjs Normal file
View File

@ -0,0 +1,24 @@
import * as child_process from 'node:child_process';
// Logging
const EXIT_FAILURE = 1;
export function fail(message) {
console.error(message);
process.exit(EXIT_FAILURE);
}
export function err(message) {
fail('ERROR: ' + message);
}
export function info(message) {
console.log('INFO: ' + message);
}
// Sub-Process
export function run(command) {
try {
info('Running: ' + command.join(' '));
child_process.execFileSync(command[0], command.slice(1), {stdio: 'inherit'});
} catch (e) {
err(e);
}
}