第一阶段:基础环境配置 (MSYS2)
无论你想编译哪种版本,都必须先完成这一步。
下载并安装 MSYS2
前往 MSYS2 官网或直接下载安装包:msys2-x86_64-20240507.exe 。
https://htst.iok.la/obj/tos-cn-i-ik7evvg4ik/1431212d55ac44b89b415f3a975fa9d7.exe
安装完成后,从开始菜单启动 MSYS2 MSYS(命令行管理程序)。
更新系统环境
在命令行中输入以下命令更新包数据库:
pacman --noconfirm -Sy
安装基本运行时库:
pacman --needed --noconfirm -S bash pacman pacman-mirrors msys2-runtime
重要:关闭当前的 MSYS2 窗口并重新启动它 。
执行全面系统更新:
pacman --noconfirm -Su
再次关闭窗口,准备进入具体的编译步骤
第二阶段:选择编译目标
🎯 目标 A:编译 RetroArch 核心 (DLL)
如果你是为了在 RetroArch 模拟器中使用 FBNeo,请执行此部分。
1.启动环境
从开始菜单打开 MSYS2 MINGW64 窗口(用于编译 64 位核心)。
2.安装编译依赖工具
复制并执行以下命令安装 64 位工具链(包含 gcc, make, git 等):
pacman -S --noconfirm --needed wget git make mingw-w64-x86_64-toolchain mingw-w64-x86_64-ntldd mingw-w64-x86_64-zlib mingw-w64-x86_64-pkg-config mingw-w64-x86_64-SDL2 mingw-w64-x86_64-libxml2 mingw-w64-x86_64-freetype mingw-w64-x86_64-python3 mingw-w64-x86_64-ffmpeg mingw-w64-x86_64-drmingw
3.获取源码
拉取 FBNeo 项目代码(如果你已经有源码,可跳过此步):
git clone https://github.com/libretro/FBNeo.git
4.进入项目目录
假设源码在你的默认路径,进入目录(根据实际路径调整):
cd FBNeo
注:文档提示如果是 D 盘路径 D:\libretro-fbneo,则输入
cd /d/libretro-fbneo
5.执行编译命令
清理旧文件(可选,推荐):
make -j5 -C ./src/burner/libretro clean make -j5 -C ./src/burner/libretro generate-files-clean #此操作会清掉driverlist.h文件,编译Android到时候需要重新生成
生成必要文件:
make -j5 -C ./src/burner/libretro generate-files
开始编译(-j5 代表线程数,可根据你 CPU 核心数调整):
make -j5 -C ./src/burner/libretro
6.获取成品
编译完毕后即可在 ./src/burner/libretro 获得 fbneo_libretro.dll 核心
generate-file 会在项目根目录生成gamelist.txt 游戏列表
压缩核心大小
strip ./src/burner/libretro/fbneo_libretro.dll
编译 PC 街机独立客户端 (fbneo64.exe)
如果你想要一个独立的 .exe 程序直接运行游戏,请执行此部分。
1.启动环境
确保打开的是 MSYS2 MINGW64 窗口 。
2.安装客户端专用依赖
独立客户端需要额外的工具(如 nasm, zip 等),请执行:
pacman -S --needed msys2-devel base-devel binutils mingw-w64-x86_64-toolchain mingw-w64-x86_64-nasm zip wget
3.进入项目目录
cd FBNeo
通过 cd 命令进入 FBNeo 源码根目录(方法同上)。
4.执行编译命令 (64位正式版)
输入以下命令开始构建:
mingw32-make mingw -j 6 BUILD_X64_EXE=1 SKIPDEPEND=1 RELEASEBUILD=1
参数说明:
BUILD_X64_EXE=1: 生成 64 位程序。
RELEASEBUILD=1: 生成正式发布版(如果不加此参数可能是 Debug 版)。
5.获取成品
编译成功后,在项目根目录下会生成 fbneo64.exe 。
6.压缩体积 (可选)
如果觉得生成的 exe 文件太大,可以安装 UPX 进行压缩:
安装 UPX:
pacman -S upx
执行压缩:
upx --best --lzma fbneo64.exe
文件路径在msys64\home\Administrator
在 Windows 10 上使用 MSYS2(推荐使用 UCRT64 或 MINGW64 终端)编译 EmulatorJS/build 的详细步骤:
1. 准备 MSYS2 环境
打开 MSYS2 的终端(推荐 MSYS2 UCRT64 或 MSYS2 MINGW64),首先更新系统并安装必要的依赖包。
执行以下命令安装依赖:
pacman -Syu pacman -S git mingw-w64-ucrt-x86_64-python3 mingw-w64-ucrt-x86_64-jq mingw-w64-ucrt-x86_64-cmake make zip p7zip wget base-devel mingw-w64-x86_64-jq make
注意:p7zip-full 在 MSYS2 中通常叫 p7zip,python3 需要安装对应环境的版本。
2. 获取代码
如果还没下载代码,先克隆仓库:
git clone https://github.com/EmulatorJS/build.git emulatorjs-build cd emulatorjs-build
3. 修改并配置 Emscripten (build_env.sh)
你提供的 build_env.sh 脚本在 Windows/MSYS2 下可能会因为路径或符号链接问题报错。我们需要手动执行这部分,或者稍微修改一下逻辑。
建议直接手动执行以下命令来替代运行 build_env.sh:
# 1. 确保在项目根目录 rm -rf emsdk git clone https://github.com/emscripten-core/emsdk.git # 2. 安装 Emscripten cd emsdk ./emsdk install 3.1.74 ./emsdk activate 3.1.74 # 3. 设置环境变量(这一步很关键) source ./emsdk_env.sh # 4. 回到根目录 cd ..
5. 开始编译
在完成了上述依赖安装和环境变量配置后,你可以开始编译了。
编译所有核心:
cd emulatorjs-build source ~/emsdk/emsdk_env.sh make clean # 运行构建脚本,编译所有核心 bash build.sh
我们最好只编译fbneo,执行下面命令:
bash build.sh --core=fbneo
编译结束后,emulatorjs-build\output目录下的fbneo.zip就是我们编译好的文件。
windows 10上build.sh修复版
#!/bin/bash
# This script is used to build the cores for RetroArch using EmulatorJS
# It will pull the cores.json file from the root of the repository and build the cores
# argument defaults
coreToBuild=""
listAllCores=false
listCoreNames=false
# process arguments
for i in "$@"
do
case $i in
-c=*|--core=*)
# core to build
coreToBuild="${i#*=}"
shift
;;
-l|--list)
# list cores
listAllCores=true
;;
--core-names-only)
# list core names only
listCoreNames=true
;;
*)
# unknown option
;;
esac
done
# set up paths
initialPath="$PWD"
buildPath="$PWD/compile"
outputPath="$PWD/output"
buildReport="$outputPath/reports"
logPath="$outputPath/logs"
outPath="$buildPath/RetroArch/emulatorjs"
tempPath="RetroArch/emulatorjs/core-temp"
# ---------------------------------------------------------------------------
# [FIX 1] Define Override Arguments
# Explicitly set compilers to use commands from PATH instead of absolute Windows paths
# AR=emar and LD=em++ are critical for correct linking in MSYS2
# ---------------------------------------------------------------------------
OVERRIDE_ARGS="CC=emcc CXX=em++ AR=emar LD=em++"
build() {
rm -f *.bc
# [FIX 2] Use /usr/bin/make to avoid Windows path issues
emmake /usr/bin/make -f "$makefileName" clean
emmake /usr/bin/make -j$(nproc --all) -f "$makefileName" platform=emscripten $OVERRIDE_ARGS $makefileArg || exit 1
linkerfilename=( *.bc )
mv $linkerfilename "$buildPath/$tempPath/normal/"
}
buildThreads() {
rm -f *.bc
emmake /usr/bin/make -f "$makefileName" clean
emmake /usr/bin/make -j$(nproc --all) -f "$makefileName" platform=emscripten EMULATORJS_THREADS=1 $OVERRIDE_ARGS $makefileArg || exit 1
linkerfilename=( *.bc )
mv $linkerfilename "$buildPath/$tempPath/threads/"
}
buildLegacy() {
rm -f *.bc
emmake /usr/bin/make -f "$makefileName" clean
emmake /usr/bin/make -j$(nproc --all) -f "$makefileName" platform=emscripten EMULATORJS_LEGACY=1 $OVERRIDE_ARGS $makefileArg || exit 1
linkerfilename=( *.bc )
mv $linkerfilename "$buildPath/$tempPath/legacy/"
}
buildThreadsLegacy() {
rm -f *.bc
emmake /usr/bin/make -f "$makefileName" clean
emmake /usr/bin/make -j$(nproc --all) -f "$makefileName" platform=emscripten EMULATORJS_THREADS=1 EMULATORJS_LEGACY=1 $OVERRIDE_ARGS $makefileArg || exit 1
linkerfilename=( *.bc )
mv $linkerfilename "$buildPath/$tempPath/legacyThreads/"
}
# create compile directory
mkdir -p $buildPath
cd $buildPath
# create output path
mkdir -p $outputPath
cp $initialPath/cores.json $outputPath
mkdir -p $buildReport
mkdir -p $logPath
if [ "$listAllCores" = false ]; then
# start pulling sources and compile
# Note: Using hub.dd.ci mirror as per your previous logs for better connectivity
if [ ! -d "RetroArch" ]; then
git clone --depth 1 -b next "https://hub.dd.ci/EmulatorJS/RetroArch.git" "RetroArch" || exit 1
fi
cd RetroArch
git pull
if [ ! -d "EmulatorJS" ]; then
git clone "https://hub.dd.ci/EmulatorJS/EmulatorJS.git" "EmulatorJS" --depth 1 || exit 1
fi
cd EmulatorJS
git pull
cd "$outPath"
rm -f *.bc
fi
compileProject() {
name="$1"
repo="$2"
branch="$3"
makefilePath="$4"
makefileName="$5"
makefileArg="$6"
custom="$7"
build_command="$8"
requireThreads="$9"
if [ ! -d "$name" ]; then
git clone "$repo" "$name" --depth 1
cd "$name"
git submodule update --init --recursive
cd ../
fi
cd "$name"
if [ $branch != 'null' ]; then
echo "Checking out branch $branch"
git checkout "$branch"
fi
git pull
git submodule update --recursive
if [[ "$custom" = "true" ]]; then
eval "$build_command"
else
cd "$makefilePath"
# only build the unthreaded version if requireThreads is false
if [ "$requireThreads" = false ]; then
build
buildLegacy
fi
# build the threaded version
buildThreads
buildThreadsLegacy
fi
cd "$buildPath"
}
cd "$buildPath"
compileStartPath="$PWD"
for row in $(jq -r '.[] | @base64' ../cores.json); do
# function to decode base64 and parse JSON
# [FIX 4] Added tr -d '\r' to clean Windows carriage returns
_jq() {
echo ${row} | tr -d '\r' | base64 --decode | jq -r ${1}
}
# get core name
name=`echo $(_jq '.') | jq -r '.name'`
# check if we need to build this core, if coreToBuild is not set, build all cores
if [ ! -z "$coreToBuild" -a "$coreToBuild" != " " ]; then
if [ "$coreToBuild" != "$name" ]; then
continue
fi
fi
# get the core details
repo=`echo $(_jq '.') | jq -r '.repo'`
branch=`echo $(_jq '.') | jq -r '.branch'`
license=`echo $(_jq '.') | jq -r '.license'`
buildpath=`echo $(_jq '.') | jq -r '.makeoptions.buildpath'`
makescript=`echo $(_jq '.') | jq -r '.makeoptions.makescript'`
arguments=`echo $(_jq '.') | jq -r '.makeoptions.arguments[] | @base64'`
options=`echo $(_jq '.') | jq -r '.options'`
optRequireThreads=`echo $(_jq '.') | jq -r '.options.requireThreads'`
custom=`echo $(_jq '.') | jq -r '.makeoptions.custom'`
build_command=`echo $(_jq '.') | jq -r '.makeoptions.build_command'`
build_retroarch_command=`echo $(_jq '.') | jq -r '.makeoptions.build_retroarch_command'`
argumentstring=""
for rowarg in $(echo "${arguments}"); do
# [FIX 5] Added tr -d '\r' here as well
argumentstring="$argumentstring `echo $rowarg | tr -d '\r' | base64 --decode`"
done
# display core details
if [ "$listCoreNames" = true ]; then
if [ "$listAllCores" = true ]; then
echo $name
fi
if [ "$listAllCores" = false ]; then
$listCoreNames=false
fi
fi
# set requireThreads to false if it's not true
if [ -z "$optRequireThreads" ] || [ "$optRequireThreads" != "true" ]; then
requireThreads=false
else
requireThreads=true
fi
if [ "$listCoreNames" = false ]; then
echo "Core: $name"
echo "Repo: $repo"
echo "Branch: $branch"
echo "License: $license"
echo "Build path: $buildpath"
echo "Make script: $makescript"
echo "Arguments: $argumentstring"
echo "Options: $options"
echo "Require threads: $requireThreads"
echo "Custom: $custom"
echo "Build command: $build_command"
echo "Build RetroArch command: $build_retroarch_command"
echo "---------------------------------------"
fi
# build if listAllCores is set to false
if [ "$listAllCores" = false ]; then
echo "Building core $name"
# create temp directory for core
cd "$buildPath"
rm -fr $tempPath
mkdir -p $tempPath/
cd $tempPath
mkdir -p normal/
mkdir -p threads/
mkdir -p legacy/
mkdir -p legacyThreads/
cd "$buildPath"
# start compile
startTime=`date -u -Is`
cd $compileStartPath
echo "Working dir $PWD"
unset FROZEN_CACHE
compileProject "$name" "$repo.git" "$branch" "$buildpath" "$makescript" "$argumentstring" "$custom" "$build_command" "$requireThreads" >> "$logPath/$name-compile.log"
# write JSON stanza for this core to disk
# [FIX 6] Added tr -d '\r' here too
echo ${row} | tr -d '\r' | base64 --decode > "./core.json"
if [ ! -z "$license" -a "$license" != " " ]; then
# license file is provided - copy it
echo "License file: $name/$license"
cp $name/$license "./license.txt"
fi
echo "Building wasm's for core $name"
cd "$buildPath/RetroArch/emulatorjs"
if [[ "$custom" = "true" ]]; then
eval "$build_retroarch_command" >> "$logPath/$name-emake.log"
else
# [FIX 3 - CRITICAL]
# 1. Use 'env' to force CC/CXX variables, preventing internal makefiles from using Windows paths.
# 2. Use 'bash' explicitly to run the .sh script on Windows.
if [ "$requireThreads" = false ]; then
mv core-temp/normal/*.bc ./
emmake /usr/bin/env CC=emcc CXX=em++ AR=emar LD=em++ bash ./build-emulatorjs.sh --clean >> "$logPath/$name-emake.log"
rm -f *.bc
mv core-temp/legacy/*.bc ./
emmake /usr/bin/env CC=emcc CXX=em++ AR=emar LD=em++ bash ./build-emulatorjs.sh --clean --legacy >> "$logPath/$name-emake.log"
rm -f *.bc
fi
mv core-temp/threads/*.bc ./
emmake /usr/bin/env CC=emcc CXX=em++ AR=emar LD=em++ bash ./build-emulatorjs.sh --clean --threads >> "$logPath/$name-emake.log"
rm -f *.bc
mv core-temp/legacyThreads/*.bc ./
emmake /usr/bin/env CC=emcc CXX=em++ AR=emar LD=em++ bash ./build-emulatorjs.sh --clean --threads --legacy >> "$logPath/$name-emake.log"
rm -f *.bc
rm -rf core-temp
fi
rm -f *.bc
echo "Packing core information for $name"
cd $compileStartPath
if [ $requireThreads = false ]; then
if [ -f "EmulatorJS/data/cores/$name-wasm.data" ]; then
7z a -t7z EmulatorJS/data/cores/$name-wasm.data ./core.json ./license.txt ../build.json
cp EmulatorJS/data/cores/$name-wasm.data $outputPath
fi
if [ -f "EmulatorJS/data/cores/$name-legacy-wasm.data" ]; then
7z a -t7z EmulatorJS/data/cores/$name-legacy-wasm.data ./core.json ./license.txt ../build.json
cp EmulatorJS/data/cores/$name-legacy-wasm.data $outputPath
fi
fi
if [ -f "EmulatorJS/data/cores/$name-thread-wasm.data" ]; then
7z a -t7z EmulatorJS/data/cores/$name-thread-wasm.data ./core.json ./license.txt ../build.json
cp EmulatorJS/data/cores/$name-thread-wasm.data $outputPath
fi
if [ -f "EmulatorJS/data/cores/$name-thread-legacy-wasm.data" ]; then
7z a -t7z EmulatorJS/data/cores/$name-thread-legacy-wasm.data ./core.json ./license.txt ../build.json
cp EmulatorJS/data/cores/$name-thread-legacy-wasm.data $outputPath
fi
# create zip file containing the core data files
cd EmulatorJS/data/cores
zip $outputPath/$name.zip $name-thread*wasm.data
if [ $requireThreads = false ]; then
zip -u $outputPath/$name.zip $name-wasm.data $name-legacy-wasm.data
fi
cd $compileStartPath
# clean up to make sure the next build in the json gets the right license and core file
rm -f ./license.txt
rm -f ./core.json
# write report to report file
endTime=`date -u -Is`
reportString="{ \"core\": \"$name\", \"buildStart\": \"$startTime\", \"buildEnd\": \"$endTime\", \"options\": $options }"
buildReportFile="$buildReport/$name.json"
echo $reportString > $buildReportFile
fi
done
if [ "$listAllCores" = false ]; then
# delete all compile files
if [[ -z "$DEPLOY_ENV" ]]; then
echo "Not deleting build path"
else
rm -fR $buildPath
fi
fi
cd "$initialPath"
同样的下面是build-emulatorjs.sh在windows 10上编译的修复版
#!/bin/bash
set +e
. ../version.all
if [[ -z "$EMSCRIPTEN" ]] ; then
echo "Run this script with emmake. Ex: emmake $0"
exit 1
fi
for i in "$@"; do
case $i in
--threads)
PTHREADS=YES
shift
;;
--legacy)
LEGACY=YES
shift
;;
--clean)
CLEAN=YES
shift
;;
*)
echo "Unknown option $i"
echo "Usage: $0 [option] ..."
echo "Options:"
echo " --threads"
echo " --legacy"
echo " --clean"
exit 1
;;
esac
done
clean () {
# [FIX 1] Force use of MSYS2 make
/usr/bin/make -C ../ -f Makefile.emulatorjs clean || exit 1
}
containsElement () {
local e match="$1"
shift
for e; do
if [[ "$e" == "$match" ]]; then
echo 1
return 0
fi
done
echo 0
return 1
}
if [[ "$CLEAN" = "YES" ]]; then
clean
fi
lastGles=0
largeStack=("mupen64plus_next")
largeHeap=("mupen64plus_next" "picodrive" "pcsx_rearmed" "genesis_plus_gx" "genesis_plus_gx_wide" "mednafen_psx" "mednafen_psx_hw" "parallel_n64" "ppsspp")
needsGles3=("ppsspp")
needsThreads=("ppsspp")
largeThreads=("ppsspp")
noCHD=("mame2003" "mame2003_plus" "pcsx_rearmed" "genesis_plus_gx" "genesis_plus_gx_wide")
no7Zip=("bsnes")
for f in $(ls -v *_emscripten.bc); do
name=`echo "$f" | sed "s/\(_libretro_emscripten\|\).bc$//"`
async=1
sevenZip=1
wasm=1
gles3=1
stack_mem=4194304 # 4mb
heap_mem=134217728 # 128mb
pthread=0
chd=1
threads=0
if [ "$LEGACY" = "YES" ]; then
gles3=0
fi
if [[ "$PTHREADS" = "YES" ]]; then
threads=1
pthread=4
fi
if [[ $(containsElement $name "${largeStack[@]}") = 1 ]]; then
stack_mem=134217728 # 128mb
fi
if [[ $(containsElement $name "${largeHeap[@]}") = 1 ]]; then
heap_mem=536870912 # 512mb
fi
if [[ $(containsElement $name "${needsThreads[@]}") = 1 && $pthread = 0 ]]; then
echo "$name"' requires threads! Please build with --threads! Exiting...'
exit 1
fi
if [[ $(containsElement $name "${noCHD[@]}") = 1 ]]; then
chd=0
fi
if [[ $(containsElement $name "${largeThreads[@]}") = 1 ]]; then
pthread=32
fi
if [[ $(containsElement $name "${no7Zip[@]}") = 1 ]]; then
sevenZip=0
fi
if [[ $(containsElement $name "${needsGles3[@]}") = 1 && $gles3 = 0 ]]; then
echo "$name"' does not support gles2 (legacy)! Please build without --legacy! Exiting...'
exit 1
fi
echo "-- Building core: $name --"
cp -f "$f" ../libretro_emscripten.a
echo NAME: $name
echo ASYNC: $async
echo HAVE_THREADS: $threads
echo PTHREAD_POOL_SIZE: $pthread
echo GLES3: $gles3
echo STACK_SIZE: $stack_mem
echo INITIAL_HEAP: $heap_mem
echo HAVE_CHD: $chd
echo HAVE_7ZIP: $sevenZip
if [[ "$CLEAN" = "YES" ]]; then
if [ $lastGles != $gles3 ] ; then
clean
fi
fi
lastGles=$gles3
# Compile core
# [FIX 2] Use /usr/bin/make and nproc --all
# [FIX 3] Explicitly pass CC/CXX variables to override environment pollution
echo "BUILD COMMAND: /usr/bin/make -C ../ -f Makefile.emulatorjs HAVE_7ZIP=$sevenZip HAVE_CHD=$chd HAVE_THREADS=$threads PTHREAD_POOL_SIZE=$pthread ASYNC=$async HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem TARGET=${name}_libretro.js -j"$(nproc --all)
/usr/bin/make -C ../ -f Makefile.emulatorjs HAVE_7ZIP=$sevenZip HAVE_CHD=$chd HAVE_THREADS=$threads PTHREAD_POOL_SIZE=$pthread ASYNC=$async HAVE_OPENGLES3=$gles3 STACK_SIZE=$stack_mem INITIAL_HEAP=$heap_mem TARGET=${name}_libretro.js CC=emcc CXX=em++ AR=emar LD=em++ -j$(nproc --all) || exit 1
# Move executable files
out_dir="../../EmulatorJS/data/cores"
out_name=""
mkdir -p $out_dir
core=""
if [ $name = "mednafen_vb" ]; then
core="beetle_vb"
else
core=${name}
fi
out_name=${core}
if [[ $pthread != 0 ]] ; then
out_name="${out_name}-thread"
fi
if [[ $gles3 = 0 ]] ; then
out_name="${out_name}-legacy"
fi
out_name="${out_name}-wasm.data"
if [ $wasm = 0 ]; then
7z a ${out_dir}/${out_name} ../${name}_libretro.js.mem ../${name}_*.js
rm ../${name}_libretro.js.mem
else
7z a ${out_dir}/${out_name} ../${name}_libretro.wasm ../${name}_*.js
rm ../${name}_libretro.wasm
fi
rm -f ../${name}_libretro.js
done
路径是emulatorjs-build\compile\RetroArch\emulatorjs