How to Update shadps4-emu Dependencies: Managing Git Submodules and CMake FetchContent

Updating shadps4-emu dependencies requires synchronizing Git submodules located in the externals/ directory and reconfiguring the CMake build system to recognize new library versions.

The shadPS4 PlayStation 4 emulator manages its third-party code primarily through Git submodules, with one exception handled via CMake's FetchContent module. Whether you are building from source for development or pulling recent changes, keeping these dependencies synchronized prevents build failures and ensures access to the latest upstream fixes.

Understanding the Dependency Architecture

According to the shadps4-emu/shadPS4 source code, the project stores almost all third-party libraries as Git submodules under the externals/ path. The .gitmodules file at the repository root defines each submodule's URL, local path, and shallow clone settings. The only dependency not managed as a submodule is Z-lib, which the project fetches via CMake's FetchContent declarative API defined in externals/CMakeLists.txt at lines 47-53.

The root CMakeLists.txt integrates these dependencies into the build by calling add_subdirectory() on the externals/ directory, making proper submodule initialization critical for compilation.

Initial Clone with Dependencies

Before updating, ensure your local repository includes all submodules. If you have not yet cloned the repository, use the recursive flag to fetch every dependency immediately:

git clone --recursive https://github.com/shadps4-emu/shadPS4.git
cd shadPS4

If you previously cloned without the --recursive flag, initialize the submodules manually:

git submodule update --init --recursive

Step-by-Step Dependency Update Workflow

1. Synchronize Submodule URLs

When the upstream .gitmodules file changes—adding new dependencies or updating repository URLs—you must synchronize your local configuration:

git submodule sync

This updates the .git/config entries to match the remote's .gitmodules definitions.

2. Update to Recorded Commits

To checkout the exact dependency versions referenced by the current commit of the main repository (the safest update method), run:

git submodule update --init --recursive

This command updates each submodule to the specific commit hash recorded in the super-project's index, ensuring a reproducible build matching the upstream CI configuration.

3. Upgrade Individual Dependencies

To pull newer upstream changes for a specific library—such as the fmt formatting library—navigate into the submodule directory and fetch directly:

cd externals/fmt
git fetch origin
git checkout main
git pull
cd ../../
git add externals/fmt
git commit -m "Update fmt to latest"

Repeat this pattern for any specific externals/<library> directory you wish to advance beyond the pinned commit.

4. Bulk Update All Dependencies

To advance every submodule to the tip of its default branch simultaneously (useful for testing bleeding-edge compatibility):

git submodule foreach 'git checkout $(git rev-parse --abbrev-ref HEAD); git pull'
git add externals/*
git commit -m "Upgrade all third-party dependencies"

Warning: Bulk updates may introduce breaking API changes. Always perform a clean build after executing this workflow to verify compatibility.

Reconfiguring the CMake Build

After updating submodules, regenerate the build cache so CMake detects new headers, library paths, and version changes:

cmake -S . -B build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
cmake --build build --parallel$(nproc)

Skipping this reconfiguration step often results in linker errors or missing symbol definitions because CMake caches the previous dependency locations.

Managing the FetchContent Exception (Z-lib)

Unlike other dependencies, Z-lib is not a Git submodule. The project declares this dependency in externals/CMakeLists.txt using FetchContent_Declare. To update Z-lib to a newer release:

  1. Edit the FetchContent_Declare block in externals/CMakeLists.txt
  2. Modify the GIT_TAG or URL parameter to point to the desired version
  3. Delete the existing build directory to clear the CMake cache
  4. Re-run the CMake configuration command

This mechanism downloads and builds Z-lib during the configuration phase, separate from the submodule workflow.

Summary

  • Git submodules in the externals/ directory manage the majority of shadps4-emu dependencies, controlled via the .gitmodules file
  • Use git submodule update --init --recursive to synchronize with the upstream project's pinned dependency versions
  • Use git submodule foreach with git pull to bulk-update all libraries to their latest branch heads, though this risks API breakage
  • Always reconfigure CMake after dependency updates to regenerate build files with new library paths
  • Z-lib is the sole exception, managed via CMake FetchContent in externals/CMakeLists.txt rather than as a submodule

Frequently Asked Questions

Why does my build fail after updating dependencies?

Build failures typically occur when CMake retains cached references to old submodule paths or when a bulk update introduced incompatible API changes. Resolve this by deleting your build directory (rm -rf build/) and reconfiguring from scratch with cmake -S . -B build. This forces CMake to redetect all externals/ directories and recompile against the updated headers.

How do I update only one specific dependency?

Navigate into the specific submodule directory (e.g., cd externals/sdl2), checkout the target branch, pull the latest changes, then return to the repository root to stage and commit the submodule pointer update. This targeted approach isolates changes and simplifies debugging compared to bulk updates.

What is the difference between git submodule update and pulling inside the submodule?

git submodule update --init --recursive checks out the exact commit hash recorded in the main repository's index, ensuring a reproducible build. Conversely, entering a submodule and running git pull fetches the latest upstream commit on the current branch, which may differ from the version the shadPS4 maintainers have tested and pinned.

How do I add a new third-party library to the project?

New dependencies require editing the .gitmodules file to add the submodule entry, running git submodule add <url> externals/<name>, and updating externals/CMakeLists.txt to include the new directory via add_subdirectory() if it is not header-only. This pattern follows the existing architecture used for libraries like fmt and zlib-ng.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →