Other Build Ecosystems
Maven is commonly used for Java projects.
What this module covers
Maven: POM, lifecycle, goals, plugins, dependency scopes Gradle: build scripts, tasks, plugins, dependency configurations npm scripts and package scripts MSBuild and .sln / .csproj Python packaging and build backends Rust Cargo and Cargo.toml Go build tooling and modules
Bazel: workspaces/modules, targets, rules, sandboxing, remote cache
Buck/Pants concepts and large-repository builds When to use a native build tool versus a meta-build tool
6.1 Maven
Maven is commonly used for Java projects. The pom.xml describes project metadata, dependencies, plugins, and build configuration.
_Not run — Maven is not installed on the build machine._ Maven phases invoke goals through lifecycle bindings. Plugin versions should be pinned for controlled builds. The example omits dependencies and may need a compiler plugin configuration for a specific environment.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>sensor-app</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
</properties>
</project>mvn validate
mvn compile
mvn test
mvn package
mvn verify
mvn clean6.2 Gradle
Gradle models work as tasks. A simplified Groovy DSL example:
_Not run — Gradle is not installed on the build machine._ Use the Gradle Wrapper (gradlew / gradlew.bat) so the project specifies the Gradle distribution it expects. Dependency locking and verification can further improve repeatability.
plugins {
id 'java'
}
group = 'com.example'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
tasks.test {
useJUnitPlatform()
}./gradlew tasks
./gradlew build
./gradlew test
./gradlew clean6.3 npm scripts
Terminal session (npm 11.16, Windows) with the package.json above (plus name/version), and no dependencies installed. The npm ci usage text after the error is trimmed:
npm ci installs from the lockfile and is intended for clean automated environments. Script names and commands depend on the project. Do not assume every project uses Vite, TypeScript, or Vitest.
{
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"test": "vitest run",
"lint": "eslint ."
}
}npm ci
npm run build
npm test$ npm run
Lifecycle scripts included in [email protected]:
test
vitest run
available via `npm run`:
dev
vite
build
tsc -b && vite build
lint
eslint .
$ npm ci
npm error code EUSAGE
npm error The `npm ci` command can only install with an existing package-lock.json or
npm error npm-shrinkwrap.json with lockfileVersion >= 1. Run an install with npm@5 or
npm error later to generate a package-lock.json file, then try again.
[exit status 1]
$ npm run build
> [email protected] build
> tsc -b && vite build
'tsc' is not recognized as an internal or external command,
operable program or batch file.
[exit status 1]6.4 MSBuild
MSBuild is used by many .NET and Visual Studio projects. A .csproj commonly declares target framework, package references, compile settings, and build properties.
Terminal session (.NET SDK 10.0.401, Windows). The only SDK installed is .NET 10, so net8.0 was changed to net10.0; the project holds one class, Sensor.cs. dotnet test was not run — it needs a separate test project:
Choose target frameworks supported by the installed SDK and your deployment environment.
The .csproj above sets no <OutputType>, so it builds a class library — a Program.cs with top-level statements fails with error CS8805 until <OutputType>Exe</OutputType> is added.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>dotnet restore
dotnet build
dotnet test
dotnet publish -c Release$ dotnet --version
10.0.401
$ dotnet restore
Determining projects to restore...
Restored <project>/SensorApp.csproj (in …).
$ dotnet build
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed …
$ dotnet publish -c Release
$ ls bin/Release/net10.0/publish
SensorApp.deps.json
SensorApp.dll
SensorApp.pdb6.5 Python build packaging
Modern Python packaging commonly separates project metadata (pyproject.toml) from the build backend. Example metadata:
_Not run — the build and setuptools packages are not installed on the build machine._
A build frontend such as python -m build can create source and wheel distributions when the required package is installed. Pin and isolate tooling for controlled release builds.
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "sensor-utils"
version = "1.0.0"
requires-python = ">=3.10"6.6 Cargo
Rust uses Cargo.toml:
_Not run — no Rust toolchain on the build machine._ Cargo.lock records resolved dependencies. For applications, committing the lockfile is commonly recommended; library policies can differ depending on team goals.
[package]
name = "sensor-demo"
version = "0.1.0"
edition = "2021"
[dependencies]cargo check
cargo build
cargo test
cargo build --release6.7 Go
Go modules are declared in go.mod:
Terminal session (Go 1.23.4, Windows) with the go.mod above, a sensor.go and a sensor_test.go:
Pin the Go toolchain where practical and understand how module versions and checksums are resolved.
go vet catches mistakes that still compile. Adding a file with a mismatched format verb:
Terminal session (Go 1.23.4, Windows):
module example.com/sensor-demo
go 1.22go test ./...
go build ./...
go vet ./...$ go version
go version go1.23.4 windows/amd64
$ cat go.mod
module example.com/sensor-demo
go 1.22
$ go test ./...
ok example.com/sensor-demo (cached)
$ go build ./...
$ go vet ./...$ go build ./...
$ go vet ./...
# example.com/sensor-demo
# [example.com/sensor-demo]
.\report.go:6:9: fmt.Sprintf format %d has arg fmt.Sprint(raw) of wrong type string
[exit status 1]6.8 Bazel and large builds
Bazel emphasizes explicit dependencies, declared inputs and outputs, sandboxing, caching, and remote execution. Concepts include:
Workspace/module: repository and dependency boundaries.
Target: a named buildable unit.
Rule: logic describing how a target is built.
Label: a stable target reference.
Sandbox: isolation that helps expose undeclared inputs.
Remote cache: reuse outputs across machines.
Remote execution: run eligible actions on remote workers.
Illustrative BUILD syntax:
_Not run — Bazel is not installed; as the text says, this is not a complete workspace._
Exact rule availability depends on language rules and Bazel version. Bazel projects require appropriate module/workspace configuration and toolchain setup; this snippet is illustrative rather than a complete repository.
cc_binary(
name = "hello",
srcs = ["hello.cc"],
)6.9 Choosing a tool
Choose based on language ecosystem, project size, team familiarity, platform support, dependency modeling, incremental correctness, IDE integration, remote execution needs, and long-term maintenance. Avoid adopting a complex build system solely because it is fashionable; equally, avoid scaling ad hoc shell scripts beyond their maintainable limits.
Quiz
? Which tool is a meta-build system that generates files for another tool?
- Ninja :: Ninja executes a graph; it does not generate one for another tool.
+ CMake :: It generates Ninja, Make or IDE project files.
- Make :: Make executes its own rules.
- Cargo :: Cargo builds Rust directly.
? Why use the Gradle Wrapper (`gradlew`)?
+ The project pins the Gradle version it expects, and the wrapper fetches that version. :: Everyone builds with the same Gradle.
- It makes Gradle faster on every machine. :: Speed is not its purpose.
- It removes the need for a JDK. :: Gradle still needs Java.
- It replaces dependency locking. :: Locking is a separate feature.
? What did the real `npm ci` run without a lockfile report?
- It installed the latest versions. :: That would be `npm install`.
+ An error: `npm ci` can only install with an existing lockfile. :: `ci` refuses to resolve versions on its own.
- Nothing — it succeeded. :: It exited with status 1.
- It generated a lockfile. :: `npm install` does that.