JS-Based Build Script

This commit is contained in:
TheBrokenRail 2024-02-03 21:07:53 -05:00
parent ddd9226e9e
commit 41fcc942fa
15 changed files with 231 additions and 122 deletions

View File

@ -34,12 +34,12 @@ jobs:
run: ./scripts/install-dependencies.sh ${{ matrix.arch }}
# Build
- name: Build
run: ./scripts/package.sh ${{ matrix.mode }} ${{ matrix.arch }}
run: ./scripts/build.mjs appimage ${{ matrix.mode }} ${{ matrix.arch }}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.mode }}-${{ matrix.arch }}
path: ./out/*.AppImage*
path: ./out/**/*.AppImage*
# Test Project
test:
strategy:

View File

@ -9,8 +9,8 @@ endif()
include(cmake/options/core-options.cmake)
# Build Mode
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
if(NOT DEFINED CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE)
endif()
# Start Project

View File

@ -2,6 +2,9 @@
mcpi_option(OPEN_SOURCE_ONLY "Only Install Open-Source Code (Will Result In Broken Install)" BOOL FALSE)
mcpi_option(IS_APPIMAGE_BUILD "AppImage Build" BOOL FALSE)
mcpi_option(IS_FLATPAK_BUILD "Flatpak Build" BOOL FALSE)
if(MCPI_IS_APPIMAGE_BUILD AND MCPI_IS_FLATPAK_BUILD)
message(FATAL_ERROR "Invalid Build Configuration")
endif()
# Server/Headless Builds
mcpi_option(SERVER_MODE "Server Mode" BOOL FALSE)
@ -61,3 +64,13 @@ mcpi_option(APP_TITLE "App Title" STRING "${DEFAULT_APP_TITLE}")
# Skin Server
mcpi_option(SKIN_SERVER "Skin Server" STRING "https://raw.githubusercontent.com/MCPI-Revival/Skins/data")
# QEMU
if(BUILD_NATIVE_COMPONENTS)
include(CheckSymbolExists)
check_symbol_exists("__ARM_ARCH" "" MCPI_IS_ARM32_OR_ARM64_TARGETING)
set(MCPI_USE_QEMU TRUE)
if(MCPI_IS_ARM32_OR_ARM64_TARGETING)
set(MCPI_USE_QEMU FALSE)
endif()
endif()

View File

@ -9,6 +9,7 @@ macro(setup_toolchain target)
add_target_variant(unknown)
add_target_variant(none)
add_target_variant(pc)
# Find Compiler
macro(find_compiler output name)
set(possible_names "")
@ -26,13 +27,11 @@ macro(setup_toolchain target)
endmacro()
find_compiler(CMAKE_C_COMPILER "gcc")
find_compiler(CMAKE_CXX_COMPILER "g++")
# Extra
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Custom Search Paths
if(NOT DEFINED ENV{MCPI_TOOLCHAIN_USE_DEFAULT_SEARCH_PATHS})
# Find Root
set(CMAKE_FIND_ROOT_PATH "/usr/${target}" "/usr/lib/${target}" "/usr")
# pkg-config
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/${target}/pkgconfig:/usr/${target}/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig")
endif()
set(CMAKE_FIND_ROOT_PATH "/usr/${target}" "/usr/lib/${target}" "/usr")
# pkg-config
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/${target}/pkgconfig:/usr/${target}/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig")
endmacro()

View File

@ -13,11 +13,11 @@ if(BUILD_NATIVE_COMPONENTS AND NOT MCPI_SERVER_MODE)
add_subdirectory(zenity)
endif()
# LIEF
if(BUILD_NATIVE_COMPONENTS OR (BUILD_ARM_COMPONENTS AND NOT MCPI_SERVER_MODE AND NOT MCPI_USE_MEDIA_LAYER_PROXY))
if(BUILD_NATIVE_COMPONENTS OR (BUILD_MEDIA_LAYER_CORE AND NOT MCPI_HEADLESS_MODE))
add_subdirectory(LIEF)
endif()
# QEMU
if(BUILD_NATIVE_COMPONENTS AND NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "arm*" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64"))
if(BUILD_NATIVE_COMPONENTS AND MCPI_USE_QEMU)
add_subdirectory(qemu)
endif()
# GLFW

View File

@ -9,14 +9,5 @@
## Instructions
```sh
./scripts/build.sh <client|server> <armhf|arm64|i686|amd64>
./scripts/build.mjs <none|appimage|flatpak> <client|server> <armhf|arm64|amd64|host> [--clean] [--install] [-Dvar=value...]
```
### Custom CMake Arguments
```sh
./scripts/setup.sh <client|server> <armhf|arm64|i686|amd64> <Custom CMake Arguments>
./scripts/build.sh <client|server> <armhf|arm64|i686|amd64>
```
### Environment Variables
* `MCPI_TOOLCHAIN_USE_DEFAULT_SEARCH_PATHS`: Use Default CMake Search Paths Rather Than Guessing

View File

@ -10,10 +10,6 @@
#define MCPI_BINARY "minecraft-pi"
#define QEMU_BINARY "qemu-arm"
#ifndef __ARM_ARCH
#define USE_QEMU
#endif
#define REQUIRED_PAGE_SIZE 4096
#define _STR(x) #x
#define STR(x) _STR(x)
@ -163,7 +159,7 @@ void pre_bootstrap(int argc, char *argv[]) {
sigaction(SIGTERM, &act_sigterm, NULL);
// Check Page Size (Not Needed When Using QEMU)
#ifndef USE_QEMU
#ifndef MCPI_USE_QEMU
long page_size = sysconf(_SC_PAGESIZE);
if (page_size != REQUIRED_PAGE_SIZE) {
ERR("Invalid page size! A page size of %ld bytes is required, but the system size is %ld bytes.", (long) REQUIRED_PAGE_SIZE, page_size);
@ -338,7 +334,7 @@ void bootstrap(int argc, char *argv[]) {
new_args[argv_start] = new_mcpi_exe_path;
// Non-ARM Systems Need QEMU
#ifdef USE_QEMU
#ifdef MCPI_USE_QEMU
argv_start--;
new_args[argv_start] = QEMU_BINARY;
// Use 4k Page Size
@ -349,7 +345,7 @@ void bootstrap(int argc, char *argv[]) {
setup_exec_environment(1);
// Pass LD_* Variables Through QEMU
#ifdef USE_QEMU
#ifdef MCPI_USE_QEMU
char *qemu_set_env = NULL;
#define pass_variable_through_qemu(name) string_append(&qemu_set_env, "%s%s=%s", qemu_set_env == NULL ? "" : ",", name, getenv(name));
for_each_special_environmental_variable(pass_variable_through_qemu);

View File

@ -12,3 +12,4 @@
#cmakedefine MCPI_VARIANT_NAME "@MCPI_VARIANT_NAME@"
#cmakedefine MCPI_SDK_DIR "@MCPI_SDK_DIR@"
#cmakedefine MCPI_SKIN_SERVER "@MCPI_SKIN_SERVER@"
#cmakedefine MCPI_USE_QEMU

View File

@ -28,7 +28,7 @@ CUSTOM_VTABLE(chat_screen, Screen) {
self->super.m_textInputs->push_back(self->chat);
self->chat->init(super->font);
self->chat->setFocused(true);
// Determien Max Length
// Determine Max Length
std::string prefix = _chat_get_prefix(Strings_default_username);
int max_length = MAX_CHAT_MESSAGE_LENGTH - prefix.length();
self->chat->setMaxLength(max_length);

View File

@ -1,8 +1,9 @@
// Config Needs To Load First
#include <libreborn/libreborn.h>
#include "game-mode-internal.h"
// Game Mode UI Code Is Useless In Headless Mode
#ifndef MCPI_SERVER_MODE
#ifndef MCPI_HEADLESS_MODE
#include <string>
#include <set>
@ -11,7 +12,6 @@
#include <mods/text-input-box/TextInputScreen.h>
#include <mods/touch/touch.h>
#include "game-mode-internal.h"
// Strings
#define GAME_MODE_STR(mode) ("Game Mode: " mode)

196
scripts/build.mjs Executable file
View File

@ -0,0 +1,196 @@
#!/usr/bin/env node
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);
}
// Enums
function Enum(values) {
for (const value of values) {
this[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',
'AppImage',
'Flatpak'
]));
const Variants = wrap(new Enum([
'Client',
'Server'
]));
const Architectures = wrap(new Enum([
'AMD64',
'ARM64',
'ARMHF',
'Host'
]));
// 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
const POSITIONAL_ARGUMENT_COUNT = 3;
function readArg(from, type) {
// Check Argument Count
if (argIndex >= process.argv.length) {
err('Expecting ' + type);
}
// Read Argument
const arg = process.argv[argIndex++];
const value = from.get(arg);
if (value === null) {
err(`Invalid ${type}: ${arg}`);
}
// Return
return value;
}
// Type Of Packaging
const packageType = readArg(PackageTypes, 'Package Type');
// Build Variant
const variant = readArg(Variants, 'Variant');
// Build Architecture
const architecture = readArg(Architectures, 'Architecture');
// Flatpak Builds Work Best Without Custom Toolchains
if (packageType === PackageTypes.Flatpak && architecture !== Architecture.Host) {
err('Flatpak Builds Do Not Support Custom Toolchains');
}
// CMake Build Options
const options = {};
// Other Arguments
let clean = false;
let install = false;
for (; argIndex < process.argv.length; argIndex++) {
const arg = process.argv[argIndex];
if (arg.startsWith('-D')) {
// Pass Build Option To CMake
let parsedArg = arg.substring(2);
const split = parsedArg.indexOf('=');
if (split === -1) {
err('Unable To Parse Build Option: ' + arg);
}
const name = parsedArg.substring(0, split);
const value = parsedArg.substring(split + 1);
if (!/^[a-zA-Z_]+$/.test(name) || name.length === 0) {
err('Invalid Build Option Name: ' + name);
}
options[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) {
err('AppImages Cannot Be Installed');
}
install = true;
} else {
err('Invalid Argument: ' + arg);
}
}
// Update Folders
function updateDir(dir) {
if (packageType !== PackageTypes.None) {
dir = path.join(dir, packageType.name);
}
return path.join(dir, variant.name, architecture.name);
}
build = updateDir(build);
out = updateDir(out);
// Configure Build Options
function toCmakeBool(val) {
return val ? 'ON' : 'OFF';
}
options['MCPI_SERVER_MODE'] = toCmakeBool(variant === Variants.Server);
options['MCPI_IS_APPIMAGE_BUILD'] = toCmakeBool(packageType === PackageTypes.AppImage);
options['MCPI_IS_FLATPAK_BUILD'] = toCmakeBool(packageType === PackageTypes.Flatpak);
if (architecture !== Architectures.Host) {
options['CMAKE_TOOLCHAIN_FILE'] = path.join(root, 'cmake', 'toolchain', architecture.name + '-toolchain.cmake');
} else {
delete options['CMAKE_TOOLCHAIN_FILE'];
}
if (packageType === PackageTypes.AppImage) {
options['CPACK_PACKAGE_DIRECTORY'] = out;
}
// Make Build Directory
function createDir(dir, clean) {
if (clean) {
fs.rmSync(dir, {recursive: true, force: true});
}
fs.mkdirSync(dir, {recursive: true});
}
createDir(build, clean);
if (!install) {
createDir(out, true);
}
// 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 cmake = ['cmake', '-GNinja'];
for (const option in options) {
cmake.push(`-D${option}=${options[option]}`);
}
cmake.push(root);
run(cmake);
// Build
run(['cmake', '--build', '.']);
// Package
if (packageType !== PackageTypes.AppImage) {
if (!install) {
process.env.DESTDIR = out;
}
run(['cmake', '--install', '.']);
} else {
run(['cmake', '--build', '.', '--target', 'package']);
}

View File

@ -1,28 +0,0 @@
#!/bin/sh
set -e
# Variables
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
ARCH="$(echo "$2" | tr '[:upper:]' '[:lower:]')"
# Run CMake If Needed
if [ ! -f "build/${MODE}-${ARCH}/build.ninja" ]; then
./scripts/setup.sh "${MODE}" "${ARCH}"
fi
# Use Build Dir
cd "build/${MODE}-${ARCH}"
# Create Prefix
if [ -z "${DESTDIR+x}" ]; then
export DESTDIR="$(cd ../../; pwd)/out/${MODE}-${ARCH}"
rm -rf "${DESTDIR}"
mkdir -p "${DESTDIR}"
fi
# Build
cmake --build .
cmake --install .
# Exit
cd ../../

View File

@ -1,20 +0,0 @@
#!/bin/sh
set -e
# Prepare
NAME='minecraft-pi-reborn'
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
ARCH="$(echo "$2" | tr '[:upper:]' '[:lower:]')"
# Build
./scripts/setup.sh "${MODE}" "${ARCH}" -DMCPI_IS_APPIMAGE_BUILD=ON
./scripts/build.sh "${MODE}" "${ARCH}"
# Package
cd "build/${MODE}-${ARCH}"
rm -f *.AppImage*
cmake --build . --target package
# Copy Output
cp *.AppImage* ../../out

View File

@ -1,38 +0,0 @@
#!/bin/sh
set -e
# Variables
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
ARCH="$(echo "$2" | tr '[:upper:]' '[:lower:]')"
shift 2
# Verify Mode
if [ "${MODE}" != "client" ] && [ "${MODE}" != "server" ]; then
echo "Invalid Mode: ${MODE}" > /dev/stderr
exit 1
fi
# Find Toolchain
toolchain_file="$(pwd)/cmake/toolchain/${ARCH}-toolchain.cmake"
if [ ! -f "${toolchain_file}" ]; then
echo "Invalid Architecture: ${ARCH}" > /dev/stderr
exit 1
fi
# Create Build Dir
rm -rf "build/${MODE}-${ARCH}"
mkdir -p "build/${MODE}-${ARCH}"
cd "build/${MODE}-${ARCH}"
# Server Build
server_mode='OFF'
if [ "${MODE}" = "server" ]; then
server_mode='ON'
fi
# Build Components
cmake -GNinja -DCMAKE_TOOLCHAIN_FILE="${toolchain_file}" -DMCPI_SERVER_MODE="${server_mode}" "$@" ../../
# Exit
cd ../../

View File

@ -4,14 +4,13 @@ set -e
# Variables
MODE="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
ARCH="$(dpkg-architecture -qDEB_BUILD_ARCH)"
ARCH='host'
# Build
./scripts/setup.sh "${MODE}" "${ARCH}" -DMCPI_HEADLESS_MODE=ON
./scripts/build.sh "${MODE}" "${ARCH}"
./scripts/build.mjs none "${MODE}" "${ARCH}" -DMCPI_HEADLESS_MODE=ON
# Add To PATH
export PATH="$(pwd)/out/${MODE}-${ARCH}/usr/bin:${PATH}"
export PATH="$(pwd)/out/${MODE}/${ARCH}/usr/bin:${PATH}"
# Make Test Directory
rm -rf build/test