There is a large and complex Gradle build system and the Android Gradle Plugin (AGP) to install most of the Android SDK components required for your application. But what if you don't want to manually install Android Studio, Android SDK, CMake, scrcpy, and other necessary tools? This is where mise https://mise.jdx.dev/ comes in—despite its apparent simplicity, it can do everything we need.
Installing mise itself
By default, the documentation suggests the following installation method:
curl https://mise.run | sh
For those who do not like this installation method, alternative options are available: https://mise.jdx.dev/installing-mise.html
You will also need to activate mise in your shell, which allows mise to function similarly to direnv. I have not tested the instructions in this article without this one-time activation.
For bash:
echo 'eval "$(mise activate bash)"' >> ~/.bashrc
For fish:
echo 'mise activate fish | source' >> ~/.config/fish/config.fish
For zsh:
echo 'eval "$(mise activate zsh)"' >> "${ZDOTDIR-$HOME}/.zshrc"
And don't forget to restart your terminal for these settings to take effect.
Basic Capabilities
If desired, mise allows you to install software globally; the list of available packages can be checked with the mise registry command.
mise use -g bun deno go java gradle
But let's not dwell on the basics. How can we use mise to simply download my project—let's say https://github.com/lunardoesdev/raytris—and build it for Android, without having anything on our system other than mise itself? Let's clone the project (if you don't have git, you can just download the archive from GitHub's website):
satori@fedora ~ [1]> git clone --depth=1 'https://github.com/lunardoesdev/raytris'
Cloning into 'raytris'...
remote: Enumerating objects: 33, done.
remote: Counting objects: 100% (33/33), done.
remote: Compressing objects: 100% (24/24), done.
remote: Total 33 (delta 0), reused 27 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (33/33), 1.39 MiB | 1.02 MiB/s, done.
satori@fedora ~> cd raytris/
mise ERROR error parsing config file: ~/raytris/mise.toml
mise ERROR Config files in ~/raytris/mise.toml are not trusted.
Trust them with `mise trust`. See https://mise.en.dev/cli/trust.html for more information.
mise ERROR Version: 2026.7.2 linux-x64 (2026-07-07)
mise ERROR Run with --verbose or MISE_VERBOSE=1 for more information
mise warns that it will not automatically install dependencies and add them to your PATH when entering the directory without your permission, which you grant using the mise trust command. After running this command, you can immediately run the mise apk task defined in my mise.toml and build the APK! Yes, it's that simple.
Breakdown of the Reproducible Environment
Let's look at the mise.toml itself (below, I will show snippets from this file and explain what they do):
[tools]
android-sdk = "latest"
"github:Genymobile/scrcpy" = "4.0"
"github:cesarferreira/byedroid" = "0.9.0"
gradle = "9.6.1"
java = "26.0.1"
zig = "0.15.1"
[env]
_.path = {tools = true, path = [
"${ANDROID_HOME}/platform-tools",
"${ANDROID_HOME}/build-tools/36.0.0",
]}
CPM_SOURCE_CACHE = "${HOME}/.cache/cpm-source-cache"
[settings]
env_shell_expand = true
[tasks.accept-android-licenses]
sources = ['settings.gradle.kts']
run = """
yes | sdkmanager --licenses
yes | sdkmanager "cmake;4.1.2"
yes | sdkmanager "platform-tools"
"""
[tasks.debug-apk]
depends = ["accept-android-licenses"]
sources = [
'build.gradle.kts',
'gradle.properties',
'src/**',
'settings.gradle.kts',
'android-sdk-packages.txt',
'CMakeLists.txt'
]
run = "gradle assembleDebug"
alias = "apk"
[tasks.debug-native]
sources = [
'src/**/*',
'*.txt'
]
run = """
mkdir -p build/debug
cd build/debug
cmake ../..
make
"""
[tasks.debug]
depends = ['debug-native']
run = '''
./build/debug/raytris
'''
alias = 'debug'
[tasks.build-windows]
sources = [
'src/**',
'*.txt'
]
run = '''
mkdir -p build/windows
cd build/windows
cmake -DCMAKE_TOOLCHAIN_FILE=cmake/zig-toolchain.cmake \
-DZIG_TARGET=x86_64-windows \
../..
make
'''
Let's go in order. The [tools] section describes the packages that mise will install.
[tools]
android-sdk = "latest"
"github:Genymobile/scrcpy" = "4.0"
"github:cesarferreira/byedroid" = "0.9.0"
gradle = "9.6.1"
java = "26.0.1"
zig = "0.15.1"
Note that you can install not only packages defined in the mise registry, but also download them directly from GitHub releases. For example, you can globally install a decent build system like xmake like this:
mise use -g github:xmake-io/xmake[rename_exe=xmake]
You can read about rename_exe and other options and their values on the mise website, for example, for their GitHub "backend": https://mise.jdx.dev/dev-tools/backends/github.html#rename-exe
Next is [env].
[env]
_.path = {tools = true, path = [
"${ANDROID_HOME}/platform-tools",
"${ANDROID_HOME}/build-tools/36.0.0",
]}
CPM_SOURCE_CACHE = "${HOME}/.cache/cpm-source-cache"
_.path is a special way to override the PATH. tools = true means we first install the tools from the [tools] section and then override this variable. $ANDROID_HOME is defined by the android-sdk package; you don't need to do this manually.
CPM_SOURCE_CACHE is a variable for CMake CPM, a package manager for the CMake build system that I use to install raylib in my project. It prevents downloading the sources over and over again if you delete the build directory or build in different locations for different platforms, as I do.
Moving on. The [settings] section.
[settings]
env_shell_expand = true
env_shell_expand allows you to do things like this in [env]:
[env]
FOO = "hello"
BAR = "$FOO-world" # "hello-world"
BAZ = "${FOO}_suffix" # "hello_suffix"
QUX = "${UNDEF:-fallback}" # "fallback"
That is, our environment variables are expanded to their values... which is exactly how it should be, in my opinion.
Next is the [tasks] section, where we define tasks, their file dependencies, and task dependencies. mise is a powerful task runner with many options for tasks. If you're interested, check out the available options in its documentation: https://mise.jdx.dev/tasks/
[tasks.accept-android-licenses]
sources = ['settings.gradle.kts']
run = """
yes | sdkmanager --licenses
yes | sdkmanager "cmake;4.1.2"
yes | sdkmanager "platform-tools"
"""
Here, in the accept-android-licenses task, we accept SDK component licenses and install the components we need, which Gradle does not always install reliably. In this example, notice that I use a specific version of CMake, which is also specified in my Gradle files. This ensures that exact version is used, as I had issues with CPM or raylib when building with an older version of CMake (which is installed if you simply choose "cmake" instead of "cmake;4.1.2"). It took some trial and error, but in the end, the system installs the required CMake version, and Gradle picks it up even if other CMake versions are installed. I added that this task depends on the settings.gradle.kts file so that it doesn't re-run on every build.
And here is the task responsible for the mise apk command:
[tasks.debug-apk]
depends = ["accept-android-licenses"]
sources = [
'build.gradle.kts',
'gradle.properties',
'src/**',
'settings.gradle.kts',
'android-sdk-packages.txt',
'CMakeLists.txt'
]
run = "gradle assembleDebug"
alias = "apk"
It depends on the previous task and builds the APK using Gradle. I am not entirely sure about src/**—the correct glob syntax might be src/**/*—but overall, the build works for me.
The alias = "apk" allows us to run our debug-apk task simply as mise apk. Note that you should not create aliases named "run" or "watch", as they conflict with mise's own subcommands.
Next, tasks for native builds, running the native game, and building for Windows are defined in a similar manner. There are no new documentation elements here, but you can appreciate the elegance of this solution, which builds a single codebase for different operating systems.
mise is a great tool; I really enjoyed working with it. Best regards.
Top comments (0)