Update Build Script
This commit is contained in:
parent
c5f9a519c5
commit
b767b9d4ec
@ -9,7 +9,7 @@ elseif(CPACK_MCPI_ARCH STREQUAL "amd64")
|
||||
endif()
|
||||
set(RUNTIME "${CPACK_TOPLEVEL_DIRECTORY}/runtime")
|
||||
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}"
|
||||
STATUS DOWNLOAD_STATUS
|
||||
)
|
||||
@ -34,10 +34,11 @@ execute_process(
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}" "-E" "env"
|
||||
"ARCH=${APPIMAGE_ARCH}"
|
||||
"VERSION=${CPACK_MCPI_VERSION}"
|
||||
"appimagetool"
|
||||
"--updateinformation" "zsync|${CPACK_MCPI_REPO}/releases/download/latest/${CPACK_PACKAGE_FILE_NAME_ZSYNC}${CPACK_MCPI_APPIMAGE_ZSYNC_EXT}"
|
||||
"--runtime-file" "${RUNTIME}"
|
||||
"--comp" "xz"
|
||||
"--comp" "zstd"
|
||||
"${CPACK_TEMPORARY_DIRECTORY}"
|
||||
"${CPACK_PACKAGE_FILE_NAME}${CPACK_MCPI_APPIMAGE_EXT}"
|
||||
WORKING_DIRECTORY "${CPACK_PACKAGE_DIRECTORY}"
|
||||
|
@ -21,6 +21,7 @@ if(MCPI_IS_APPIMAGE_BUILD)
|
||||
macro(pass_to_cpack var)
|
||||
set("CPACK_MCPI_${var}" "${MCPI_${var}}")
|
||||
endmacro()
|
||||
pass_to_cpack(VERSION)
|
||||
pass_to_cpack(ARCH)
|
||||
pass_to_cpack(REPO)
|
||||
pass_to_cpack(APPIMAGE_EXT)
|
||||
|
@ -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:
|
||||
```sh
|
||||
sudo apt install -y libfuse2 libopenal1 libglib2.0-0
|
||||
sudo apt install -y libopenal1 libglib2.0-0
|
||||
```
|
||||
</details>
|
||||
|
||||
|
@ -2,113 +2,28 @@
|
||||
import * as path from 'node:path';
|
||||
import * as url from 'node:url';
|
||||
import * as fs from 'node:fs';
|
||||
import * as child_process from 'node:child_process';
|
||||
|
||||
// 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);
|
||||
}
|
||||
import { info, err, run } from './lib/util.mjs';
|
||||
import { parseOptions, Enum, Architectures } from './lib/options.mjs';
|
||||
|
||||
// Enums
|
||||
function Enum(values) {
|
||||
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([
|
||||
const PackageTypes = new Enum([
|
||||
'None',
|
||||
'AppImage',
|
||||
'Flatpak'
|
||||
]));
|
||||
const Architectures = wrap(new Enum([
|
||||
'AMD64',
|
||||
'ARM64',
|
||||
'ARMHF',
|
||||
'Host'
|
||||
]));
|
||||
const Configurations = wrap(new Enum([
|
||||
'Release',
|
||||
'Debug'
|
||||
]));
|
||||
]);
|
||||
|
||||
// Folders
|
||||
const __filename = url.fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const root = path.join(__dirname, '..');
|
||||
let build = path.join(root, 'build');
|
||||
let out = path.join(root, 'out');
|
||||
|
||||
// Positional Arguments
|
||||
let argIndex = 2; // Skip First Two Arguments
|
||||
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];
|
||||
// CMake Options
|
||||
const cmakeArgPrefix = '-D';
|
||||
if (arg.startsWith(cmakeArgPrefix)) {
|
||||
const cmakeArgSeparator = '=';
|
||||
const cmakeOptions = new Map();
|
||||
function parseCMakeOption(arg) {
|
||||
if (arg === null) {
|
||||
// Usage Text
|
||||
return `${cmakeArgPrefix}var${cmakeArgSeparator}value...`;
|
||||
} else if (arg.startsWith(cmakeArgPrefix)) {
|
||||
// Pass Build Option To CMake
|
||||
let parsedArg = arg.substring(cmakeArgPrefix.length);
|
||||
const parts = parsedArg.split('=');
|
||||
const parts = parsedArg.split(cmakeArgSeparator);
|
||||
if (parts.length !== 2) {
|
||||
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) {
|
||||
err('Invalid Build Option Name: ' + name);
|
||||
}
|
||||
options.set(name, value);
|
||||
} else if (arg === '--clean') {
|
||||
// Remove Existing Build Directory
|
||||
clean = true;
|
||||
} else if (arg === '--install') {
|
||||
// Install To System Instead Of Output Directory
|
||||
if (packageType === PackageTypes.AppImage) {
|
||||
cmakeOptions.set(name, value);
|
||||
return true;
|
||||
} else {
|
||||
// Unknown Option
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Options
|
||||
const options = parseOptions([
|
||||
['packageType', PackageTypes],
|
||||
['architecture', Architectures]
|
||||
], [
|
||||
'clean',
|
||||
'install',
|
||||
'debug'
|
||||
], parseCMakeOption);
|
||||
|
||||
// Check Options
|
||||
if (options.packageType === PackageTypes.Flatpak && options.architecture !== Architectures.Host) {
|
||||
err('Flatpak Builds Do Not Support Custom Toolchains');
|
||||
}
|
||||
if (options.packageType === PackageTypes.AppImage && options.install) {
|
||||
err('AppImages Cannot Be Installed');
|
||||
}
|
||||
install = true;
|
||||
} else if (arg === '--config') {
|
||||
// Set Configuration
|
||||
config = parseArg(process.argv[++argIndex], Configurations, 'Configuration');
|
||||
} else {
|
||||
// Invalid
|
||||
err('Invalid Argument: ' + arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Update Folders
|
||||
function updateDir(dir) {
|
||||
if (packageType !== PackageTypes.None) {
|
||||
dir = path.join(dir, packageType.name);
|
||||
}
|
||||
return path.join(dir, architecture.name);
|
||||
}
|
||||
build = updateDir(build);
|
||||
let cleanOut = false;
|
||||
// AppImages Are Placed Directly In ./out
|
||||
if (packageType !== PackageTypes.AppImage) {
|
||||
cleanOut = true;
|
||||
out = updateDir(out);
|
||||
}
|
||||
// Folders
|
||||
const __filename = url.fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const root = path.join(__dirname, '..');
|
||||
let build = path.join(root, 'build');
|
||||
let out = path.join(root, 'out');
|
||||
|
||||
// Configure Build Options
|
||||
function toCmakeBool(val) {
|
||||
return val ? 'ON' : 'OFF';
|
||||
// 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));
|
||||
options.set('MCPI_IS_FLATPAK_BUILD', toCmakeBool(packageType === PackageTypes.Flatpak));
|
||||
if (architecture !== Architectures.Host) {
|
||||
options.set('CMAKE_TOOLCHAIN_FILE', path.join(root, 'cmake', 'toolchain', architecture.name + '-toolchain.cmake'));
|
||||
build = specializeDir(build);
|
||||
// Update Output Directory
|
||||
const useOutRoot = options.packageType === PackageTypes.AppImage;
|
||||
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 {
|
||||
options.delete('CMAKE_TOOLCHAIN_FILE');
|
||||
cmakeOptions.delete(toolchainOption);
|
||||
}
|
||||
|
||||
// Make Build Directory
|
||||
@ -170,39 +103,31 @@ function createDir(dir, clean) {
|
||||
}
|
||||
fs.mkdirSync(dir, {recursive: true});
|
||||
}
|
||||
createDir(build, clean);
|
||||
if (!install) {
|
||||
createDir(out, cleanOut);
|
||||
createDir(build, options.clean);
|
||||
if (!options.install) {
|
||||
createDir(out, !useOutRoot);
|
||||
}
|
||||
|
||||
// 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'];
|
||||
options.forEach((value, key, map) => {
|
||||
configure.push(`-D${key}=${value}`);
|
||||
cmakeOptions.forEach((value, key) => {
|
||||
configure.push(cmakeArgPrefix + key + cmakeArgSeparator + value);
|
||||
});
|
||||
configure.push(root);
|
||||
configure.push('-S', root, '-B', build);
|
||||
run(configure);
|
||||
|
||||
// Build
|
||||
const configArg = ['--config', config.prettyName];
|
||||
run(['cmake', '--build', '.', ...configArg/*, '-v'/*, '--', '-n', '-j1', '-j1'*/]);
|
||||
const configArg = ['--config', options.debug ? 'Debug' : 'Release'];
|
||||
run(['cmake', '--build', build, ...configArg]);
|
||||
|
||||
// Package
|
||||
if (packageType !== PackageTypes.AppImage) {
|
||||
if (!install) {
|
||||
if (options.packageType !== PackageTypes.AppImage) {
|
||||
if (!options.install) {
|
||||
process.env.DESTDIR = out;
|
||||
}
|
||||
run(['cmake', '--install', '.', ...configArg]);
|
||||
run(['cmake', '--install', build, ...configArg]);
|
||||
} else {
|
||||
run(['cmake', '--build', '.', '--target', 'package', ...configArg]);
|
||||
run(['cmake', '--build', build, '--target', 'package', ...configArg]);
|
||||
// Copy Generated Files
|
||||
const files = fs.readdirSync(build);
|
||||
for (const file of files) {
|
||||
|
@ -61,19 +61,19 @@ run_build() {
|
||||
"libxext-dev:$1" \
|
||||
`# QEMU Dependencies` \
|
||||
"libglib2.0-dev:$1" \
|
||||
`# AppStream Verification` \
|
||||
appstream
|
||||
`# AppImage` \
|
||||
appstream \
|
||||
zsync
|
||||
|
||||
# Install appimagetool
|
||||
sudo rm -rf /opt/squashfs-root /opt/appimagetool /usr/local/bin/appimagetool
|
||||
case "$(dpkg --print-architecture)" in
|
||||
'armhf') APPIMAGE_ARCH='armhf';;
|
||||
'arm64') APPIMAGE_ARCH='aarch64';;
|
||||
'i386') APPIMAGE_ARCH='i686';;
|
||||
'amd64') APPIMAGE_ARCH='x86_64';;
|
||||
esac
|
||||
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
|
||||
# Workaround AppImage Issues With Docker
|
||||
sudo ./scripts/fix-appimage-for-docker.sh /opt/appimagetool
|
||||
@ -83,7 +83,8 @@ run_build() {
|
||||
sudo rm -f ./appimagetool
|
||||
# Link
|
||||
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
|
||||
|
91
scripts/lib/options.mjs
Normal file
91
scripts/lib/options.mjs
Normal 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
24
scripts/lib/util.mjs
Normal 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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user