macos build script + build workflows

This commit is contained in:
otavepto 2024-05-12 04:11:50 +03:00
parent 574f9f6376
commit e6291d1f22
3 changed files with 709 additions and 0 deletions

View File

@ -0,0 +1,70 @@
name: Build all emu variants (macos)
on:
workflow_call:
# needed since it allows this to become a reusable workflow
workflow_dispatch:
# allows manual trigger
permissions:
contents: write
jobs:
deps:
name: Restore or build deps
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-deps-macos.yml
emu-regular-x64-release:
name: Regular x64 (release)
needs: [ deps ]
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-build-variant-macos.yml
with:
emu-variant: regular
debug: false
emu-regular-x64-debug:
name: Regular x64 (debug)
needs: [ deps ]
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-build-variant-macos.yml
with:
emu-variant: regular
debug: true
emu-exp-x64-release:
name: Experimental x64 (release)
needs: [ deps ]
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-build-variant-macos.yml
with:
emu-variant: exp
debug: false
emu-exp-x64-debug:
name: Experimental x64 (debug)
needs: [ deps ]
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-build-variant-macos.yml
with:
emu-variant: exp
debug: true
tools-x64-release:
name: Tools x64 (release)
needs: [ deps ]
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-build-variant-macos.yml
with:
emu-variant: tools
debug: false
tools-x64-debug:
name: Tools x64 (debug)
needs: [ deps ]
if: ${{ !cancelled() }}
uses: ./.github/workflows/emu-build-variant-macos.yml
with:
emu-variant: tools
debug: true

View File

@ -0,0 +1,111 @@
name: Build emu variant (macos)
on:
workflow_call:
inputs:
emu-variant:
description: |
Which variant of the emu to build:
regular: build the regular version of the emu
exp: build the experimental version of the emu
tools: build the tools only
required: true
type: string
debug:
description: |
build mode
true: build in debug mode
false: build in release mode
required: true
type: boolean
permissions:
contents: write
env:
DEPS_CACHE_KEY: emu-deps-macos
DEPS_CACHE_DIR: build/deps/macos
PACKAGE_BASE_DIR: "build/package/macos"
THIRD_PARTY_BASE_DIR: 'third-party'
jobs:
build:
runs-on: macos-13
if: ${{ !cancelled() }}
steps:
### setup build vars
- name: Setup build vars
shell: bash
run: |
echo "env file = '$GITHUB_ENV'"
echo "workspace = '${{ github.workspace }}'"
build_switches=""
if [[ "${{ inputs.emu-variant }}" = "regular" ]]; then
build_switches="+lib-64 +client-64"
elif [[ "${{ inputs.emu-variant }}" = "exp" ]]; then
build_switches="+exp-lib-64 +exp-client-64"
elif [[ "${{ inputs.emu-variant }}" = "tools" ]]; then
build_switches="+tool-clientldr +tool-itf-64 +tool-lobby-64"
else
echo "[X] invalid emu variant '${{ inputs.emu-variant }}'" >&2
exit 1
fi
echo "build_switches=$build_switches" >>"$GITHUB_ENV"
build_mode=""
if [[ "${{ inputs.debug }}" = "true" ]]; then
build_mode="debug"
else
build_mode="release"
fi
echo "build_mode=$build_mode" >>"$GITHUB_ENV"
- name: Checkout branch
uses: actions/checkout@v4
### deps
- name: Restore deps
id: emu-deps-cache-step
uses: actions/cache@v4
with:
key: ${{ env.DEPS_CACHE_KEY }}
path: ${{ env.DEPS_CACHE_DIR }}
## mandatory macos packages, installed via sudo apt install ...
- name: Install required packages
shell: bash
run: sudo chmod 777 build_macos_deps.sh && ./build_macos_deps.sh -verbose -packages_only
## extra helpers/tools, these are not built inside the deps build dir
- name: Clone third-party build helpers (build/macos)
uses: actions/checkout@v4
with:
ref: 'third-party/build/macos'
path: "${{env.THIRD_PARTY_BASE_DIR}}/build/macos"
### fix folder permissions! not sure why this fails
# nested subdirs "build/macos/release" cause permission problems
- name: Give all permissions to repo folder
shell: bash
working-directory: ${{ github.workspace }}
run: sudo chmod -R 777 "${{ github.workspace }}"
### build target(s)
- name: Build target(s)
shell: bash
working-directory: ${{ github.workspace }}
run: "sudo chmod 777 build_macos.sh && ./build_macos.sh -j 3 -verbose ${{ env.build_mode }} clean +build_str ${{ github.sha }} ${{ env.build_switches }}"
### upload artifact/package to github Actions (for targets)
- name: Upload build package (for targets)
uses: actions/upload-artifact@v4
with:
name: "emu-macos-${{ inputs.emu-variant }}-64-${{ env.build_mode }}-${{ github.sha }}"
path: "build/macos/"
if-no-files-found: 'error'
compression-level: 9
retention-days: 1

528
build_macos.sh Normal file
View File

@ -0,0 +1,528 @@
#!/usr/bin/env bash
### make sure to cd to the emu src dir
required_files=(
"dll/dll.cpp"
"dll/steam_client.cpp"
"controller/gamepad.c"
"sdk/steam/isteamclient.h"
)
for emu_file in "${required_files[@]}"; do
if [ ! -f "$emu_file" ]; then
echo "[X] Invalid emu directory, change directory to emu's src dir (missing file '$emu_file')" >&2
exit 1
fi
done
BUILD_LIB64=0
BUILD_EXP_LIB64=0
BUILD_CLIENT64=0
BUILD_EXPT_CLIENT64=0
BUILD_TOOL_CLIENT_LDR=0
BUILD_TOOL_FIND_ITFS64=0
BUILD_TOOL_LOBBY64=0
BUILD_LIB_NET_SOCKETS_64=0
# < 0: deduce, > 1: force
PARALLEL_THREADS_OVERRIDE=-1
CMD_BUILD_STR=''
# 0 = release, 1 = debug, otherwise error
BUILD_TYPE=-1
CLEAN_BUILD=0
VERBOSE=0
for (( i=1; i<=$#; i++ )); do
var="${!i}"
if [[ "$var" = "-j" ]]; then
i=$((i+1))
PARALLEL_THREADS_OVERRIDE="${!i}"
[[ "$PARALLEL_THREADS_OVERRIDE" =~ ^[0-9]+$ ]] || {
echo "[X] Invalid arg after -j, expected a number" >&2;
exit 1;
}
#echo "[?] Overriding parralel build jobs count with $PARALLEL_THREADS_OVERRIDE"
elif [[ "$var" = "+build_str" ]]; then
i=$((i+1))
CMD_BUILD_STR="${!i}"
[[ -z "$CMD_BUILD_STR" ]] && {
echo "[X] Expected a build string" >&2;
exit 1;
}
elif [[ "$var" = "+lib-64" ]]; then
BUILD_LIB64=1
elif [[ "$var" = "+exp-lib-64" ]]; then
BUILD_EXP_LIB64=1
elif [[ "$var" = "+client-64" ]]; then
BUILD_CLIENT64=1
elif [[ "$var" = "+exp-client-64" ]]; then
BUILD_EXPT_CLIENT64=1
elif [[ "$var" = "+tool-clientldr" ]]; then
BUILD_TOOL_CLIENT_LDR=1
elif [[ "$var" = "+tool-itf-64" ]]; then
BUILD_TOOL_FIND_ITFS64=1
elif [[ "$var" = "+tool-lobby-64" ]]; then
BUILD_TOOL_LOBBY64=1
elif [[ "$var" = "+lib-netsockets-64" ]]; then
BUILD_LIB_NET_SOCKETS_64=1
elif [[ "$var" = "-verbose" ]]; then
VERBOSE=1
elif [[ "$var" = "clean" ]]; then
CLEAN_BUILD=1
elif [[ "$var" = "release" ]]; then
BUILD_TYPE=0
elif [[ "$var" = "debug" ]]; then
BUILD_TYPE=1
else
echo "[X] Invalid arg: $var" >&2
exit 1
fi
done
# use 70%
build_threads="$(( $(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 0) * 70 / 100 ))"
[[ $PARALLEL_THREADS_OVERRIDE -gt 0 ]] && build_threads="$PARALLEL_THREADS_OVERRIDE"
[[ $build_threads -lt 1 ]] && build_threads=1
# build type
optimize_level=""
dbg_level=""
dbg_defs=""
linker_strip_dbg_symbols=''
build_folder=""
if [[ "$BUILD_TYPE" = "0" ]]; then
optimize_level="-O2"
dbg_level="-g0"
dbg_defs="-DEMU_RELEASE_BUILD -DNDEBUG"
linker_strip_dbg_symbols='-Wl,-S'
build_folder="release"
elif [[ "$BUILD_TYPE" = "1" ]]; then
optimize_level="-Og"
dbg_level="-g3"
dbg_defs=""
linker_strip_dbg_symbols=""
build_folder="debug"
else
echo "[X] You must specify any of: [release debug]" >&2
exit 1
fi
build_root_dir="build/macos/$build_folder"
build_root_64="$build_root_dir/x64"
build_root_experimental="$build_root_dir/experimental"
build_root_tools="$build_root_dir/tools"
# common stuff
deps_dir="build/deps/macos"
libs_dir="libs"
tools_dir='tools'
build_temp_dir="build/tmp/macos"
protoc_out_dir="dll/proto_gen/macos"
third_party_dir="third-party"
third_party_build_dir="$third_party_dir/build/macos"
protoc_exe_64="$deps_dir/protobuf/install64/bin/protoc"
parallel_exe="$third_party_build_dir/rush/rush"
# https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
common_compiler_args="-std=c++17 -fvisibility=hidden -fexceptions -fno-jump-tables -fno-char8_t"
# third party dependencies (include folder + folder containing .a file)
ssq_inc="$deps_dir/libssq/include"
ssq_lib64="$deps_dir/libssq/build64"
curl_inc64="$deps_dir/curl/install64/include"
curl_lib64="$deps_dir/curl/install64/lib"
protob_inc64="$deps_dir/protobuf/install64/include"
protob_lib64="$deps_dir/protobuf/install64/lib"
zlib_inc64="$deps_dir/zlib/install64/include"
zlib_lib64="$deps_dir/zlib/install64/lib"
mbedtls_inc64="$deps_dir/mbedtls/install64/include"
mbedtls_lib64="$deps_dir/mbedtls/install64/lib"
overlay_inc64="$deps_dir/ingame_overlay/install64/include"
overlay_lib64="$deps_dir/ingame_overlay/install64/lib"
overlay_sys_lib64="$deps_dir/ingame_overlay/deps/System/install64/lib"
overlay_detour_lib64="$deps_dir/ingame_overlay/deps/mini_detour/install64/lib"
# directories to use for #include
release_incs_both=(
"$ssq_inc"
"$libs_dir"
"$protoc_out_dir"
"$libs_dir/utfcpp"
"controller"
"dll"
"sdk"
"overlay_experimental"
"crash_printer"
"helpers"
)
release_incs64=(
"${release_incs_both[@]}"
"$curl_inc64"
"$protob_inc64"
"$zlib_inc64"
"$mbedtls_inc64"
)
# directories where libraries (.a or .so) will be looked up
release_libs_dir64=(
"$ssq_lib64"
"$curl_lib64"
"$protob_lib64"
"$zlib_lib64"
"$mbedtls_lib64"
"$overlay_lib64"
"$overlay_sys_lib64"
"$overlay_detour_lib64"
)
# libraries to link with, either static ".a" or dynamic ".so" (name only)
# if it's called libXYZ.a, then only write "XYZ"
# for static libs make sure to build a PIC lib
# each will be prefixed with -l, ex: -lpthread
release_libs=(
"pthread"
"dl"
"ssq"
"z" # libz library
"curl"
"protobuf-lite"
"mbedcrypto"
)
# common source files used everywhere, just for convinience, you still have to provide a complete list later
release_src=(
"dll/*.cpp"
"$protoc_out_dir/*.cc"
"crash_printer/linux.cpp"
"helpers/common_helpers.cpp"
)
emu_build_string="$CMD_BUILD_STR"
[[ -z "$emu_build_string" ]] && {
emu_build_string="$(date +'%m%d%Y-%H%M')"
}
# https://gcc.gnu.org/onlinedocs/gcc/Preprocessor-Options.html
# additional #defines
common_defs="-DGNUC -DUTF_CPP_CPLUSPLUS=201703L -DCURL_STATICLIB -D'EMU_BUILD_STRING=$emu_build_string'"
release_defs="$dbg_defs $common_defs"
# errors to ignore during build
release_ignore_warn="-Wno-switch"
[ ! -d "$deps_dir" ] && {
echo "[X] Dependencies dir \"$deps_dir\" was not found" >&2;
exit 1;
}
[ ! -f "$protoc_exe_64" ] && {
echo "[X] protobuff compiler wasn't found - 64" >&2;
exit 1;
}
[ ! -f "$parallel_exe" ] && {
echo "[X] tool to run parallel compilation jobs '$parallel_exe' wasn't found" >&2;
exit 1;
}
chmod 777 "$parallel_exe"
echo "[?] All build operations will use $build_threads parallel jobs"
last_code=0
empty_arr=()
function build_for () {
local is_exe=$( [[ "$1" != 0 ]] && { echo 1; } || { echo 0; } )
local out_filepath="${2:?'missing output filepath'}"
local -n all_src=$3
local -n extra_inc_dirs=$4
local extra_defs="$5"
local -n extra_libs=$6
[[ "${#all_src[@]}" = "0" ]] && {
echo [X] "No source files specified" >&2;
return 1;
}
# https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
local cpiler_pic_pie='-fPIC'
[[ $is_exe = 1 ]] && cpiler_pic_pie='-fPIE'
# https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
# https://www.baeldung.com/linux/library-convert-static-to-shared
local linker_pic_pie='-shared'
[[ $is_exe = 1 ]] && linker_pic_pie='-pie'
local tmp_work_dir="${out_filepath##*/}"
tmp_work_dir="$build_temp_dir/${tmp_work_dir%.*}"
echo " -- Cleaning compilation directory '$tmp_work_dir/'"
rm -f -r "$tmp_work_dir"
mkdir -p "$tmp_work_dir" || {
echo [X] "Failed to create compilation directory" >&2;
return 1;
}
local build_cmds=()
local -A objs_dirs=()
for src_file in $( echo "${all_src[@]}" ); do
[[ -f "$src_file" ]] || {
echo "[X] source file "$src_file" wasn't found" >&2;
return 1;
}
# https://stackoverflow.com/a/9559024
local obj_dir=$( [[ -d "${src_file%/*}" ]] && echo "${src_file%/*}" || echo '.' )
obj_dir="$tmp_work_dir/$obj_dir"
local obj_name="${src_file##*/}"
obj_name="${obj_name%.*}.o"
build_cmds+=("$src_file<>$obj_dir/$obj_name")
objs_dirs["$obj_dir"]="$obj_dir"
mkdir -p "$obj_dir"
done
[[ "${#build_cmds[@]}" = "0" ]] && {
echo [X] "No valid source files were found" >&2;
return 1;
}
local target_incs=()
local target_libs_dirs=()
target_incs=("${release_incs64[@]}")
target_libs_dirs=("${release_libs_dir64[@]}")
target_incs=(
"${target_incs[@]}"
"${extra_inc_dirs[@]}"
)
# wrap each -ImyIncDir with single quotes -> '-ImyIncDir'
target_incs=("${target_incs[@]/#/\'-I}")
target_incs=("${target_incs[@]/%/\'}")
echo " -- Compiling object files with $build_threads parallel jobs inside directory '$tmp_work_dir/'"
local build_cmd="clang++ -c -x c++ $common_compiler_args $cpiler_pic_pie $optimize_level $dbg_level $release_ignore_warn $release_defs $extra_defs ${target_incs[@]} '{1}' '-o{2}'"
if [[ $VERBOSE = 1 ]]; then
printf '%s\n' "${build_cmds[@]}" | "$parallel_exe" --dry-run -j$build_threads -d '<>' -k "$build_cmd"
echo;
fi
printf '%s\n' "${build_cmds[@]}" | "$parallel_exe" -j$build_threads -d '<>' -k "$build_cmd"
exit_code=$?
echo " -- Exit code = $exit_code"
echo; echo;
sleep 1
[[ $exit_code = 0 ]] || {
rm -f -r "$tmp_work_dir";
sleep 1
return $exit_code;
}
echo " -- Linking all object files from '$tmp_work_dir/' to produce '$out_filepath'"
local obj_files=()
for obj_file in $(echo "${objs_dirs[@]/%//*.o}" ); do
[[ -f "$obj_file" ]] && obj_files+=("$obj_file")
done
# if no files were added
[[ "${#obj_files[@]}" -gt 0 ]] || {
echo "[X] No files to link" >&2;
return 1;
}
local out_dir="${out_filepath%/*}"
[[ "$out_dir" = "$out_filepath" ]] && out_dir='.'
mkdir -p "$out_dir"
local target_libs=(
"${release_libs[@]}"
"${extra_libs[@]}"
)
# https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/developer_guide/gcc-using-libraries#gcc-using-libraries_using-both-static-dynamic-library-gcc
# https://linux.die.net/man/1/ld
if [[ $VERBOSE = 1 ]]; then
echo "clang++ $common_compiler_args $cpiler_pic_pie $optimize_level $dbg_level $linker_pic_pie $linker_strip_dbg_symbols -o" "$out_filepath" "${obj_files[@]}" "${target_libs_dirs[@]/#/-L}" "-Wl,--whole-archive -Wl,-Bstatic" "${target_libs[@]/#/-l}" "-Wl,-Bdynamic -Wl,--no-whole-archive -Wl,--exclude-libs,ALL"
echo;
fi
clang++ $common_compiler_args $cpiler_pic_pie $optimize_level $dbg_level $linker_pic_pie $linker_strip_dbg_symbols -o "$out_filepath" "${obj_files[@]}" "${target_libs_dirs[@]/#/-L}" -Wl,--whole-archive -Wl,-Bstatic "${target_libs[@]/#/-l}" -Wl,-Bdynamic -Wl,--no-whole-archive -Wl,--exclude-libs,ALL
exit_code=$?
echo " -- Exit code = $exit_code"
echo; echo;
rm -f -r "$tmp_work_dir"
sleep 1
return $exit_code
}
function cleanup () {
rm -f -r "$build_temp_dir"
}
if [[ "$CLEAN_BUILD" = "1" ]]; then
echo // cleaning previous build
[[ -d "$build_root_dir" ]] && rm -f -r "$build_root_dir"
echo; echo;
fi
## tools
if [[ "$BUILD_TOOL_CLIENT_LDR" = "1" ]]; then
[[ -d "$build_root_tools/steamclient_loader/" ]] || mkdir -p "$build_root_tools/steamclient_loader/"
cp -f "$tools_dir"/steamclient_loader/linux/* "$build_root_tools/steamclient_loader/"
echo; echo;
fi
### x64 build
cleanup
echo // invoking protobuf compiler - 64
rm -f -r "$protoc_out_dir"
mkdir -p "$protoc_out_dir"
"$protoc_exe_64" ./dll/*.proto -I./dll/ --cpp_out="$protoc_out_dir/"
last_code=$((last_code + $?))
echo; echo;
if [[ "$BUILD_LIB64" = "1" ]]; then
echo // building shared lib libsteam_api.so - 64
all_src_files=(
"${release_src[@]}"
"controller/*.c"
)
build_for 0 "$build_root_64/libsteam_api.so" all_src_files empty_arr '-DCONTROLLER_SUPPORT' empty_arr
last_code=$((last_code + $?))
fi
if [[ "$BUILD_CLIENT64" = "1" ]]; then
echo // building shared lib steamclient.so - 64
all_src_files=(
"${release_src[@]}"
"controller/*.c"
)
build_for 0 "$build_root_64/steamclient.so" all_src_files empty_arr '-DCONTROLLER_SUPPORT -DSTEAMCLIENT_DLL' empty_arr
last_code=$((last_code + $?))
fi
if [[ "$BUILD_EXP_LIB64" = "1" ]]; then
echo // building shared lib experimental libsteam_api.so - 64
all_src_files=(
"${release_src[@]}"
"controller/*.c"
"overlay_experimental/*.cpp"
)
extra_incs=(
"$overlay_inc64"
)
extra_link_libs=(
"ingame_overlay"
"system" # ingame_overlay dependency
"mini_detour" # ingame_overlay dependency
)
build_for 0 "$build_root_experimental/x64/libsteam_api.so" all_src_files extra_incs '-DCONTROLLER_SUPPORT -DEMU_OVERLAY -DImTextureID=ImU64' extra_link_libs
last_code=$((last_code + $?))
fi
if [[ "$BUILD_EXPT_CLIENT64" = "1" ]]; then
echo // building shared lib experimental steamclient.so - 64
all_src_files=(
"${release_src[@]}"
"controller/*.c"
"overlay_experimental/*.cpp"
)
extra_incs=(
"$overlay_inc64"
)
extra_link_libs=(
"ingame_overlay"
"system" # ingame_overlay dependency
"mini_detour" # ingame_overlay dependency
)
build_for 0 "$build_root_experimental/x64/steamclient.so" all_src_files extra_incs '-DCONTROLLER_SUPPORT -DSTEAMCLIENT_DLL -DEMU_OVERLAY -DImTextureID=ImU64' extra_link_libs
last_code=$((last_code + $?))
fi
if [[ "$BUILD_TOOL_LOBBY64" = "1" ]]; then
echo // building executable lobby_connect_x64 - 64
all_src_files=(
"${release_src[@]}"
"$tools_dir/lobby_connect/lobby_connect.cpp"
)
build_for 1 "$build_root_tools/lobby_connect/lobby_connect_x64" all_src_files empty_arr '-DNO_DISK_WRITES -DLOBBY_CONNECT' empty_arr
last_code=$((last_code + $?))
fi
if [[ "$BUILD_TOOL_FIND_ITFS64" = "1" ]]; then
echo // building executable generate_interfaces_file_x64 - 64
all_src_files=(
"$tools_dir/generate_interfaces/generate_interfaces.cpp"
)
build_for 1 "$build_root_tools/find_interfaces/generate_interfaces_file_x64" all_src_files empty_arr '' empty_arr
last_code=$((last_code + $?))
fi
if [[ "$BUILD_LIB_NET_SOCKETS_64" = "1" ]]; then
echo // building shared lib steamnetworkingsockets64.so - 64
all_src_files=(
"networking_sockets_lib/steamnetworkingsockets.cpp"
)
build_for 0 "$build_root_dir/networking_sockets_lib/steamnetworkingsockets64.so" all_src_files empty_arr '' empty_arr
last_code=$((last_code + $?))
fi
# cleanup
cleanup
# copy configs + examples
if [[ $last_code = 0 ]]; then
echo "// copying readmes + files examples"
mkdir -p "$build_root_dir/"
cp -f -r "post_build/steam_settings.EXAMPLE/" "$build_root_dir/"
cp -f "post_build/README.release.md" "$build_root_dir/"
cp -f "CHANGELOG.md" "$build_root_dir/"
if [[ $BUILD_TYPE = 1 ]]; then
cp -f "post_build/README.debug.md" "$build_root_dir/"
fi
if [[ -d "$build_root_tools/find_interfaces" ]]; then
mkdir -p "$build_root_tools/find_interfaces/"
cp -f "post_build/README.find_interfaces.md" "$build_root_tools/find_interfaces/"
fi
if [[ -d "$build_root_tools/lobby_connect" ]]; then
mkdir -p "$build_root_tools/lobby_connect/"
cp -f "post_build/README.lobby_connect.md" "$build_root_tools/lobby_connect/"
fi
else
echo "[X] Not copying readmes or files examples due to previous errors" >&2
fi
echo; echo;
echo;
if [[ $last_code = 0 ]]; then
echo "[GG] no failures"
else
echo "[XX] general failure" >&2
fi
exit $last_code