Gradle is an awesome build system which makes my life as a developer much easier. However, with large numbers of dependencies build times can slow to a snail’s pace, leaving developers frustrated and legitimately slacking off.
Buck
Facebook has a tool that can help many developers emerge from this time-suck. The tool is the Buck build system. Buck is built to encourage developers to create and use small and reusable modules of code. Buck has many advantages over Gradle, the most beneficial of which are support for incremental builds and shorter build times.
I’m going to walk through first steps with okbuck
to get started using it with an Android project. It’s easy to configure, and it doesn’t affect Android Gradle builds, so there’s no downside to experimenting with it.
Related: Our most popular post on Android – Replace AsyncTask and AsyncTaskLoader with Rx.Observable – RxJava Android Patterns
Buck overview
Buck generates rules based on your build’s dependencies and creates a Directed Acyclic Graph (DAG) from the rules. When a build is kicked off, it builds rules starting with the leaf nodes of the DAG. The rules are added to a queue and built when a thread is available. If a rule is successfully built, other rules that depend on it check if their dependencies are satisfied and if so, get added to the queue. This process continues until all nodes have been built.
Output from building nodes is cached to prevent the churn that Gradle presently incurs when a build is kicked off. When Buck begins building a rule, the cache is checked for previous outputs and if the output exists it is used instead of building it locally. Buck hashes the rule to create a cache key and will rebuild any rules whose key has changed preventing the need to clean the build. You can also set up Buck to use a cache that will be committed to your source control which will improve build speeds throughout your process: on developer’s machines and on continuous integration (CI), but that’s outside the scope of this blog.
For Android builds, Buck includes a customized version of the Android DX build tool (creates Dalvik Executable .dex files from Java .class files) that includes performance improvements like running multiple copies of DX concurrently and faster dex merging.
Build Rules
A build rule uses a set of input files to generate zero or one output files. Buck includes some built-in build rules for common procedures in a few major languages. In Android, for example, it’s common to build a Java library against the Android SDK as well as to produce an APK file. These rules are android_library
and android_binary
respectively. Build rules must have at least three arguments: the name of the build rule, the rule’s dependencies, and the set of build rules that are allowed to claim the rule as a dependency.
Build File
Buck uses a build file named BUCK which defines build rules. Build rules are defined using Python functions in a build file. This means that developers can define their own Python functions within a build file to add something to the build process. Source files can only be referred to by rules in the nearest build file with respect to the project’s file tree.
OkBuck
Buck configuration is verbose and time consuming. Quite ironic for a tool that speeds up Android builds significantly. To take the pain out of using Buck, Uber developed the Gradle plugin okbuck
. The plugin adds a couple of tasks to the Gradle project that makes using Buck significantly easier. These tasks generate a Buck wrapper, similar in fashion to the Gradle wrapper most Android developers are familiar with. okbuck
also takes care of generating all of the Buck configuration based on Gradle project you’re already using.
Some developers will find that using okbuck
will help them locally, but won’t want to use it on CI. Have it your way! okbuck
supplements Gradle and the two can be used on the same project. okbuck
can just be ignored if you don’t want to use it, but it does need to be added to your Gradle project in order to be used.
Installation
I’m assuming that you use Homebrew for the installation portion of this blog. If you’re not using Homebrew, please take a moment to learn about it and consider whether it will help you be more productive.
- Install Buck (optional)
If you want to play with Buck outside of a Gradle project, install Buck and go to town. okbuck
(via the buckw
wrapper) will download Buck if isn’t available.
brew update
brew tap facebook/fb
brew install buck
- Prerequisites
You’ll want to configure the okbuck
plugin for your Gradle project, which is quite easy.
Install a few tools
brew install android-ndk ant
brew install watchman
Installing the android-ndk
formula via Homebrew is not necessary if you have an Android NDK installation elsewhere. Just be certain that the ANDROID_NDK
environment variable points to your NDK installation.
- Configure
Configuring your Gradle project to use okbuck
is simple. Add a classpath
dependency and apply the plugin in your top-level build.gradle
file, done.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.uber:okbuck:0.10.0'
}
}
apply plugin: 'com.uber.okbuck'
- Ensure your project’s Gradle version is compliant.
At the time of this writing, the minimum supported Gradle version is 2.14.1. Just edit the distributionUrl
property in {project}/gradle/wrapper/gradle-wrapper.properties
and change to gradle–2.14.1-all.zip
. Gradle will fail and issue a message that tells you what your currently configured version is and what the present minimum required version is.
- Generate the Buck wrapper
okbuck
will generate a Buck wrapper similar in function to the Gradle wrapper that most developers are likely familiar with: gradlew
./gradlew :buckWrapper
- Configure code signing
If you try to skip this step and build with the new Buck wrapper, you’ll quickly encounter this error.
Execution failed for task ':okbuck'.
> release of app has no signing config set!
okbuck
requires that you set up your signing config, which is a good idea anyway. Generate a keystore and configure your build appropriately.
signingConfigs {
release {
storeFile file(RELATIVE_PATH_TO_YOUR_KEYSTORE)
storePassword YOUR_STORE_PASSWORD
keyAlias YOUR_KEY_ALIAS
keyPassword YOUR_KEY_PASSWORD
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
For the purposes of writing this blog, I cheated and just used the default debug keystore to sign my release build. Don’t do this unless your project is a throw-away sandbox like mine was.
buildTypes {
release {
signingConfig signingConfigs.debug
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
- Build with Buck
Building with the Buck wrapper is easy.
./buckw build //app:bin_debug
It’s even easier if you use an alias.
./buckw build appDebug
Aliases are defined in the .buckconfig
file., okbuck
will create some default aliases in the local version of the file, .buckconfig.local
[alias]
appDebug = //app:bin_debug
appRelease = //app:bin_release
- Install/run with Buck
Installing with the Buck wrapper is just as easy, you can launch the app by tacking on the --run
flag.
./buckw install --run appDebug
This should get you started down a road to faster builds and learning more about Buck. There is a lot more to learn about Buck and I encourage you to visit https://buckbuild.com/ if you are interested in modifying the buck configuration files that okbuck
created for you.
Some of okbuck
’s currently unsupported features (Kotlin support, data binding) might be a deal breaker for some projects. Support for those features is planned for the future so don’t lose hope.