Overview #
The Brane SDK employs a powerful multi-project build system that enables you to develop, test, and deploy applications across diverse computing platforms – from standard CPUs to specialized accelerators like Kalray MPPA and FPGAs. This guide explains how the build system works, how projects are organized, and how to effectively manage your development workflow.
Key Features #
- Cross-Platform Development: Build applications for CPUs, Kalray MPPA accelerators, and FPGAs using a single toolchain
- Gradle-Based Build System: Leverage a powerful, extensible build system with hierarchical configuration
- Platform-Specific Optimizations: Access specialized tools and configurations for each target platform
- Unified Workflow: Consistent commands and processes across all platforms
Understanding the Multi-Project Structure #
At its core, the Brane SDK uses a hierarchical project organization to manage multiple applications targeting different hardware platforms. This approach strikes a balance between organization, flexibility, and reuse.
The Project Hierarchy #
When you examine the Brane SDK project structure, you’ll see a carefully designed hierarchy:
demo-project/
├── build.gradle # Root project configuration
├── settings.gradle # Defines which subprojects to include
└── Toolkit/ # Main toolkit container
└── demo/ # Demo applications
├── x86_64_CPU/ # CPU-targeted applications
│ ├── FFTConvolver/
│ └── matrixMultiply/
├── coolidge/ # Kalray MPPA applications
│ ├── FFT/
│ ├── FluidX3D/
│ └── matrixMultiplyOcl/
└── fpga/ # FPGA applications
└── gplgpu/
This structure follows a logical organization:
- The top level provides common configuration and project definitions
- The middle level separates projects by target platform
- The leaf level contains individual applications with specific implementations
This approach offers several advantages:
- You can easily find applications by their target platform
- Common settings can be shared at the appropriate level
- You can build everything at once or focus on specific platforms/applications
Project Paths: Important Note #
Throughout this documentation and in Gradle commands, projects are referenced using path notation:
:Toolkit:demo:x86_64_CPU:matrixMultiply
This path directly corresponds to the directory structure:
Toolkit/demo/x86_64_CPU/matrixMultiply
Note: When referring to projects in
include
statements withinsettings.gradle
, the root project name is included:
include 'demo-project:Toolkit:demo:x86_64_CPU:matrixMultiply'
However, when running Gradle tasks from the command line, the root project name is omitted:
./gradlew :Toolkit:demo:x86_64_CPU:matrixMultiply:build
The settings.gradle File: Defining Your Project Universe #
The settings.gradle
file serves as the blueprint for your project structure, telling Gradle which projects to include in the build:
rootProject.name = 'demo-project'
include 'demo-project:Toolkit:demo:x86_64_CPU:FFTConvolver'
include 'demo-project:Toolkit:demo:x86_64_CPU:matrixMultiply'
include 'demo-project:Toolkit:demo:coolidge:FFT'
include 'demo-project:Toolkit:demo:coolidge:FluidX3D'
include 'demo-project:Toolkit:demo:coolidge:matrixMultiplyOcl'
include 'demo-project:Toolkit:demo:fpga:gplgpu'
This file plays a crucial role by:
- Setting the root project name (
demo-project
) - Explicitly including each subproject using directory paths (with colons as separators)
- Allowing you to selectively include or exclude projects (notice the commented-out Bitcoin project)
When working with the SDK, you can easily add your own projects by adding new include
statements with the appropriate path. For example, if you wanted to add a new CPU project named “ImageProcessor”, you would add:
include 'demo-project:Toolkit:demo:x86_64_CPU:ImageProcessor'
Similarly, you can temporarily remove projects from the build by commenting out their include statements, which can be useful when you want to focus on specific projects or troubleshoot build issues.
Configuration Inheritance: The Power of Gradle Build Files #
One of the most powerful aspects of the Brane SDK build system is its use of configuration inheritance through a hierarchy of build.gradle
files. This approach follows the “Don’t Repeat Yourself” (DRY) principle, placing common configuration at higher levels while allowing specific overrides at lower levels.
Root build.gradle: Global Configuration #
The root build.gradle
file establishes settings that apply to all projects:
// Configure build script repositories and dependencies
buildscript {
repositories {
// Brane plugin repository (requires AWS credentials)
maven {
url "s3://com.brane.repository.maven/release/"
credentials(AwsCredentials) {
// AWS credentials can be provided as environment variables or in gradle.properties
accessKey = System.getenv('AWS_ACCESS_KEY_ID') ?: findProperty('aws_access_key_id')
secretKey = System.getenv('AWS_SECRET_ACCESS_KEY') ?: findProperty('aws_secret_access_key')
}
}
// Public repositories for additional dependencies
mavenCentral()
jcenter()
}
// Required dependencies for the build process
dependencies {
// Brane-Builder plugin - core of the SDK
classpath 'com.brane.plugins:brane-builder:2.0.1'
// Apache Velocity for template processing
classpath 'org.apache.velocity:velocity:1.7'
// Add any additional build-time dependencies here
}
}
// Common configurations that apply to all projects
allprojects {
repositories {
mavenCentral()
}
}
This file typically handles:
- Repository definitions (where to find dependencies)
- Global project properties (group, version)
- Common plugin applications
- Credential management
- Environment setup
Application-Specific Configuration #
Finally, each individual application has its own build.gradle with configurations specific to that application:
/*
* Copyright (C) 2018 Brane Technologies LLC - All Rights Reserved
* You may use this code under the terms of the Brane license.
*
* You should have received a copy of the Brane license with
* this file. If not, please write to: atif@branetechnologies.com; or visit http://branetechnologies.com
*
* Author: Nicolas Siret
* Contact: nicolas@branetechnologies.com
* 2018-10
*
*/
plugins {
id 'com.brane.coolidge.cpp'
}
application {
targetMachines = [
machines.linux.x86_64,
coolidgeMachines.clusterOS.architecture("MPPA_v2")
]
binaries.configureEach {
def compileTask = compileTask.get()
if (toolChain instanceof GccCompatibleToolChain) {
compileTask.compilerArgs.addAll(['-lOpenCL', '-pthread'])
}
def linkTask = linkTask.get()
if (toolChain instanceof GccCompatibleToolChain) {
linkTask.linkerArgs.addAll(['-lOpenCL', '-pthread'])
}
}
}
These application-level configurations:
- Define specific dependencies unique to this application
- Override default settings when necessary
- Add custom tasks for application-specific operations
- Configure application-specific compiler flags or options
This three-level configuration hierarchy gives you the best of both worlds: consistent settings across similar projects while still allowing customization where needed.
Building and Running Projects #
The Brane SDK’s build system, build on top of Gradle build system, provides a consistent set of commands across platforms, with platform-specific extensions where needed.

Common Build Commands #
Regardless of the target platform, you can use IntelliJ or these standard Gradle commands:

# From the root directory, build everything
./gradlew build
# Build just one platform's projects
./gradlew :DevToolkit:demo:coolidge:build
# Build one specific project
./gradlew :DevToolkit:demo:coolidge:matrixMultiplyOcl:build
# Clean the build directory
./gradlew clean
# Assemble without running tests
./gradlew assemble
The hierarchical project structure gives you precise control over what gets built, saving time during development. You can:
- Build everything when preparing a complete release
- Build just one platform when focusing on platform-specific work
- Build just one application when iterating on a specific feature
Platform-Specific Commands #
Different platforms offer specialized commands to handle their unique capabilities:
For CPU Applications #

# Run the application (standard Gradle run task)
./gradlew :DevToolkit:demo:x86_64_CPU:FFTConvolver:run
# Run with specific arguments
./gradlew :DevToolkit:demo:x86_64_CPU:FFTConvolver:run --args="--input=sample.wav --output=processed.wav"
For Coolidge (MPPA) Applications #

# Run on emulator (faster but less accurate)
./gradlew :DevToolkit:demo:coolidge:FFT:emulate
# Run with detailed simulation
./gradlew :DevToolkit:demo:coolidge:FFT:simulate
# Deploy to connected hardware
./gradlew :DevToolkit:demo:coolidge:FFT:linuxRun
./gradlew :DevToolkit:demo:coolidge:FFT:jtagRun
For FPGA Applications #

# Generate FPGA project files
./gradlew :DevToolkit:demo:fpga:gplgpu:generateProject
# Run behavioral simulation
./gradlew :DevToolkit:demo:fpga:gplgpu:check
# Synthesize the design
./gradlew :DevToolkit:demo:fpga:gplgpu:compileProject
These platform-specific commands are defined by the respective Brane SDK plugins applied in the project’s build.gradle files. They abstract away the complexity of working with different toolchains and provide a consistent interface.
Advanced Build System Features #
The Brane SDK build system includes several advanced features that help manage complex multi-platform projects.
Conditional Configuration #
You can apply configuration conditionally based on various factors:
// Apply configuration based on the build type
tasks.withType(CppCompile) { task ->
if (task.name.contains('Release')) {
// Apply optimization for release builds
task.compilerArgs.addAll(['-O3', '-march=native'])
} else {
// Enable debugging for debug builds
task.compilerArgs.addAll(['-g', '-O0'])
}
}
// Apply configuration based on the environment
if (System.getenv('CI') != null) {
// Special configuration for continuous integration environment
allprojects {
tasks.withType(Test) {
testLogging.showStandardStreams = true
}
}
}
Cross-Project Dependencies #
You can establish dependencies between projects:
// DevToolkit/demo/coolidge/FluidX3D/build.gradle
dependencies {
// Depend on another project in the build
implementation project(':DevToolkit:demo:coolidge:commonUtils')
}
This is powerful for creating shared libraries or utilities that multiple projects can use.
Environment-Specific Properties #
You can define properties specific to different environments or configurations:
// In gradle.properties
coolidge.default.processors=5
coolidge.high.processors=80
// In build.gradle
application {
// Use environment-specific configuration
def processorCount = findProperty("coolidge.${System.getenv('SCALE') ?: 'default'}.processors")
binaries.configureEach {
compileTask.get().compilerArgs.add("-DPROCESSOR_COUNT=${processorCount}")
}
}
This approach helps manage configurations across different development, testing, and production environments.
Practical Examples and Best Practices #
Let’s explore some practical examples and best practices for working with the Brane SDK build system.
Adding a New Application #
To add a new application to the SDK, e.g.
1. Create the directory structure
DevToolkit/demo/x86_64_CPU/MyNewApp/src/main/cpp
2. Add the project to settings.gradle:
include 'DevToolkit:demo:x86_64_CPU:MyNewApp'
3. Create a build.gradle file:
// DevToolkit/demo/x86_64_CPU/MyNewApp/build.gradle
plugins {
id 'com.brane.cpu.cpp-application'
}
application {
targetMachines = [machines.linux.x86_64]
}
dependencies {
// Add your dependencies here
}
4. Implement your application in src/main/cpp
5. Build and run using IntelliJ or the following commands:
./gradlew :DevToolkit:demo:x86_64_CPU:MyNewApp:build
./gradlew :DevToolkit:demo:x86_64_CPU:MyNewApp:run
Managing Build Performance #
For larger projects, build time can become an issue. Here are some strategies to improve build performance:
1. Use the Gradle Daemon: The Gradle daemon keeps a JVM running in the background to avoid startup costs.
# In gradle.properties
org.gradle.daemon=true
2. Enable Parallel Builds: Build projects in parallel when dependencies allow.
# In gradle.properties
org.gradle.parallel=true
3. Use Build Caching: Cache build outputs to reuse them when inputs haven’t changed.
# In gradle.properties
org.gradle.caching=true
4. Selective Building: Only build what you need.
# Build just what you're working on
./gradlew :DevToolkit:demo:coolidge:myProject:build
5. Configure on Demand: Only configure the projects you’re building.
# In gradle.properties
org.gradle.configureondemand=true
Troubleshooting Common Issues #
When working with the Brane SDK build system, you might encounter some common issues. Here’s how to address them:
Missing Dependencies #
Symptoms:
- Error messages like
Could not resolve artifact 'com.example:library:1.0.0'
- Build fails with
Could not find method implementation() for arguments [...]
Solutions:
Verify repository configurations:
repositories {
maven {
url "s3://com.brane.repository.maven/release/"
credentials(AwsCredentials) {
accessKey = System.getenv('AWS_ACCESS_KEY_ID')
secretKey = System.getenv('AWS_SECRET_ACCESS_KEY')
}
}
mavenCentral()
}
1. Check AWS credentials:
# Verify environment variables are set
echo $AWS_ACCESS_KEY_ID
echo $AWS_SECRET_ACCESS_KEY
2. Refresh dependencies:
./gradlew build --refresh-dependencies
3. Check dependency resolution with:
./gradlew dependencies
Incorrect Project Paths #
Symptoms:
- Error messages like
Project with path ':DevToolkit:demo:x86_64_CPU:matrixMultiply' could not be found
- Tasks not being found when attempting to execute them
Solutions:
1. Check project paths in settings.gradle
:
# Correct format
include 'demo-project:Toolkit:demo:x86_64_CPU:matrixMultiply'
2. Use the correct path format in Gradle commands:
# Correct format (note the leading colon)
./gradlew :Toolkit:demo:x86_64_CPU:matrixMultiply:build
3. List available projects:
./gradlew projects
Gradle Wrapper Issues #
Symptoms:
Permission denied
when running./gradlew
- Gradle commands fail with wrapper errors
Solutions:
1. Make the Gradle wrapper executable:
chmod +x gradlew
2. Regenerate the Gradle wrapper:
gradle wrapper --gradle-version 7.4.2
CPU Development Issues #
Symptoms:
- Compilation errors related to missing libraries
- Linking failures with undefined references
Solutions:
1. Check compiler and linker flags:
binaries.configureEach {
compileTask.get().compilerArgs.add('-std=c++17')
linkTask.get().linkerArgs.add('-pthread')
}
2. Verify library paths:
# Check library paths on Linux
echo $LD_LIBRARY_PATH
3. Install required development packages:
# On Ubuntu/Debian
sudo apt-get install libopenblas-dev libfftw3-dev
Kalray MPPA (Coolidge) Issues #
Symptoms:
- Errors related to OpenCL kernel compilation
- Runtime errors when deploying to hardware
Solutions:
1. Verify Kalray environment setup:
# Check if environment variables are set
echo $KVXOPENCLDIR
# Set up environment if needed
export KVXOPENCLDIR=/opt/kalray/accesscore/lib/target/kvx-cos
2. Check kernel file path:
// Ensure OpenCL kernels are copied to the build directory
tasks.register('copyKernels', Copy) {
from 'src/main/opencl'
into 'build/exe/main/debug'
include '*.cl'
}
3. Try using emulation first:
./gradlew :Toolkit:demo:coolidge:myApp:emulate
FPGA Development Issues #
Symptoms:
- Synthesis errors in Vivado
- Timing constraint failures
Solutions:
1. Check Xilinx environment setup:
# Check if Vivado is in PATH
echo $XILINX_VIVADO
# Set up environment if needed
export XILINX_VIVADO=/opt/Xilinx/Vivado/2022.1
2. Verify timing constraints file:
HardwarePlugin {
// Ensure constraint files are correctly specified
constraintFiles = ['constraints/timing.xdc']
}
3. Run simulation before synthesis:
./gradlew :Toolkit:demo:fpga:myApp:check
Advanced Debugging Techniques #
Using Gradle Debug Options #
For more detailed information on build failures:
# Get a stack trace for errors
./gradlew build --stacktrace
# Get info-level logging
./gradlew build --info
# Get debug-level logging (very verbose)
./gradlew build --debug
# Generate a build scan for detailed analysis
./gradlew build --scan
Inspecting Task Execution #
To understand what Gradle is doing:
# List all tasks in the project
./gradlew tasks
# Show task execution times
./gradlew build --profile
# Show task dependencies
./gradlew :Toolkit:demo:x86_64_CPU:matrixMultiply:taskName --dry-run
Working with Different Target Platforms #
The Brane SDK supports multiple target platforms, each with its own development workflow and considerations. Let’s explore the specific needs of each platform and how the build system supports them.
CPU Application Development #
CPU applications are the most straightforward, targeting standard x86_64 processors. These projects typically:
- Use C/C++ for implementation
- May leverage multi-threading, SIMD instructions, and optimized libraries
- Have simpler debugging and deployment workflows
A typical CPU application build.gradle looks like this:
plugins {
id 'com.brane.cpu.cpp-application'
}
application {
// Support both Linux and Windows x86_64
targetMachines = [
machines.linux.x86_64,
machines.windows.x86_64
]
// Configure C++ compiler settings
binaries.configureEach { binary ->
def compileTask = binary.compileTask.get()
// Enable C++17
compileTask.compilerArgs.add('-std=c++17')
// Add optimization flags for release builds
if (binary.optimized) {
compileTask.compilerArgs.addAll(['-O3', '-march=native'])
// Enable platform-specific optimizations
if (binary.targetMachine.operatingSystemFamily.linux) {
compileTask.compilerArgs.add('-flto')
}
}
}
}
dependencies {
// Common performance libraries
implementation 'org.openblas:openblas:0.3.19'
implementation 'org.fftw:fftw3:3.3.8'
}
The development workflow for CPU applications typically involves:
- Building:
./gradlew :DevToolkit:demo:x86_64_CPU:myApp:build
- Testing:
./gradlew :DevToolkit:demo:x86_64_CPU:myApp:test
- Running:
./gradlew :DevToolkit:demo:x86_64_CPU:myApp:run
- Debugging:
- Configure your IDE debugger (IntelliJ IDEA, VS Code, etc.)
- Set breakpoints and debug as normal
Best practices for CPU application development:
- Leverage platform-specific optimizations where appropriate
- Use conditional compilation for platform-specific code
- Consider portability across different operating systems
- Use performance profiling tools to identify bottlenecks
Kalray MPPA (Coolidge) Development #
Developing for the Kalray MPPA Coolidge processor introduces additional complexity due to its heterogeneous nature:
- Applications typically have a host component (running on CPU) and accelerator code (running on MPPA)
- The accelerator code is often written in OpenCL
- Development involves cross-compilation and specialized deployment
A typical Coolidge application build.gradle includes:
plugins {
id 'com.brane.coolidge.cpp'
}
application {
// Target both standard CPU and MPPA
targetMachines = [
machines.linux.x86_64,
coolidgeMachines.clusterOS.architecture("MPPA_v2")
]
// Configure binary-specific settings
binaries.configureEach { binary ->
// Add Coolidge-specific compiler flags
compileTask.get().compilerArgs.add('-DKVX_COOLIDGE')
// Link with required libraries
linkTask.get().linkerArgs.addAll(['-ldl', '-llttng-ust'])
}
}
// Ensure OpenCL kernels are copied to the build directory
tasks.register('copyKernels', Copy) {
from 'src/main/opencl'
into 'build/exe/main/debug'
include '*.cl'
}
assemble.dependsOn('copyKernels')
The Coolidge development workflow typically involves:
- Building:
./gradlew :DevToolKit:demo:coolidge:myApp:build
- Emulation (faster but less accurate):
./gradlew :DevToolKit:demo:coolidge:myApp:emulate
- Simulation (slower but more accurate):
./gradlew :DevToolKit:demo:coolidge:myApp:simulate
- Hardware Deployment:
# Over Linux network ./gradlew :DevToolKit:demo:coolidge:myApp:linuxRun # Via JTAG connection ./gradlew :DevToolKit:demo:coolidge:myApp:jtagRun
Key considerations for Coolidge development:
- Memory management between host and device
- Optimization of OpenCL kernel code for MPPA architecture
- Workgroup size tuning for maximum performance
- Careful management of cluster resources
FPGA Development #
FPGA development involves hardware design using HDLs (Hardware Description Languages) like Verilog or VHDL:
- Projects describe digital circuits rather than software algorithms
- Development involves synthesis, place-and-route, and bitstream generation
- Testing uses hardware simulation rather than direct execution
A typical FPGA application build.gradle looks like:
plugins {
id 'com.brane.hdl' // Core HDL plugin
id 'com.brane.hdl.vivado' // Xilinx Vivado integration
}
HardwarePlugin {
top = "main_design" // Top-level module name
sim = "test_main_design" // Test bench module name
revision = "rev1" // Hardware revision
// Add timing constraints
constraintFiles = ['constraints/timing.xdc']
// Set synthesis strategy
synthStrategy = "Performance_ExploreWithRemap"
}
sourceSets {
main {
verilog {
srcDirs += ['src/main/ip_cores'] // Additional source directories
}
}
}
The FPGA development workflow typically involves:
- Building Project Files:
./gradlew :DevToolkit:demo:fpga:myApp:generateProject
- Simulation:
./gradlew :DevToolkit:demo:fpga:myApp:check
- Synthesis:
./gradlew :DevToolkit:demo:fpga:myApp:compileProject
- Implementation (Place-and-Route):
./gradlew :DevToolkit:demo:fpga:myApp:assemble
- Bitstream Generation:
./gradlew :DevToolkit:demo:fpga:myApp:build
Key considerations for FPGA development:
- Timing constraints and meeting timing requirements
- Resource utilization optimization
- Test bench development for thorough verification
- IP core integration and management
Customizing the Build Process #
The Brane SDK build system is highly customizable. Here are some advanced customization techniques:
Custom Plugins #
You can create custom Gradle plugins to encapsulate common configurations:
// In buildSrc/src/main/groovy/com/yourcompany/CustomPlugin.groovy
package com.yourcompany
import org.gradle.api.Plugin
import org.gradle.api.Project
class CustomPlugin implements Plugin<Project> {
void apply(Project project) {
// Apply standard plugins
project.apply plugin: 'com.brane.cpu.cpp-application'
// Add common configuration
project.application {
targetMachines = [project.machines.linux.x86_64]
}
// Register common tasks
project.tasks.register('standardBenchmark') {
project.dependsOn('assemble')
doLast {
// Benchmark logic
}
}
}
}
// In build.gradle
apply plugin: com.yourcompany.CustomPlugin
Build Script Delegation #
For complex projects, you can split build logic into multiple script files:
// In build.gradle
apply from: 'gradle/cpp-config.gradle'
apply from: 'gradle/testing-config.gradle'
apply from: 'gradle/benchmark-tasks.gradle'
// In gradle/cpp-config.gradle
tasks.withType(CppCompile) {
// C++ configuration
}
// In gradle/testing-config.gradle
tasks.withType(Test) {
// Testing configuration
}
Continuous Integration #
The Brane SDK build system integrates well with CI/CD pipelines. Here’s how to configure it for common CI systems:
Jenkins Pipeline #
// Jenkinsfile
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './gradlew build'
}
}
stage('Test') {
steps {
sh './gradlew test'
}
}
stage('CPU Demo Apps') {
steps {
sh './gradlew :DevToolkit:demo:x86_64_CPU:build'
}
}
stage('Coolidge Simulation') {
steps {
sh './gradlew :DevToolkit:demo:coolidge:simulate'
}
}
stage('FPGA Simulation') {
steps {
sh './gradlew :DevToolkit:demo:fpga:check'
}
}
}
post {
always {
junit 'build/**/test-results/**/*.xml'
}
}
}
GitHub Actions #
# .github/workflows/build.yml
name: Build and Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
- name: Cache Gradle packages
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build
run: ./gradlew build
- name: Test CPU apps
run: ./gradlew :DevToolkit:demo:x86_64_CPU:test
Artifact Publication #
For sharing libraries or tools built with the Brane SDK:
// In build.gradle
apply plugin: 'maven-publish'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
groupId = 'com.yourcompany'
artifactId = 'awesome-library'
version = '1.0.0'
}
}
repositories {
maven {
name = 'CompanyRepository'
url = "https://repo.yourcompany.com/maven-releases"
credentials {
username = findProperty('repoUser') ?: System.getenv('REPO_USER')
password = findProperty('repoPassword') ?: System.getenv('REPO_PASSWORD')
}
}
}
}
Further Resources #
- Gradle Documentation: https://docs.gradle.org/
- Brane SDK Reference Guide: [Internal company link]
- Community Forums: [Internal company forum link]
- Support Channels: support@branetechnologies.com
Remember that the build system is just a tool to help you develop more efficiently. As you become more familiar with it, you’ll find your own patterns and practices that work best for your specific projects and team workflow.