Escape dependency hell.

Tangram is a programmable build system and package manager in which all dependencies are specified explicitly and pinned with a lockfile. You get the exact same versions of every package on every machine, so your builds are simple, reproducible, cacheable, and distributable.

Simple
Write your builds in JSON, JavaScript, or TypeScript.
Reproducible
Stop debugging errors caused by inconsistent package versions.
Cacheable
Stop building the same thing over and over again.
Distributable
Transparently offload your builds to a cluster or the cloud.
Build a shell.

Create a tangram.json file at the root of your project and add some dependencies. Now run tg shell. This will create a tangram.lock file and drop you in a shell with your dependencies in $PATH. This is a great way to make sure everyone on your team is using the exact same versions of all the tools you need to work on your project.

// tangram.json { "dependencies": { "nodejs": "16.15.1", "postgresql": "14.4", "python": "3.10.4", "ripgrep": "13.0.0" } }
$ tg shell $ node --version v16.15.1 $ postgres --version postgres (PostgreSQL) 14.4 $ python --version Python 3.10.4 $ rg --version ripgrep 13.0.0
Build your code.

Use Tangram to build both your dependencies and your code. In this example, we build a Rust project and specify the exact version of the OpenSSL C library to link to.

// tangram.json { "dependencies": { "rust": "1.60.0", "openssl": "3.0.0" } }
// tangram.Tangram.ts import { cargo } from "tangram:rust"; import openssl from "tangram:openssl"; export default ({ system }) => { return rust.cargo({ system, source: import.meta.source, nativeDependencies: { "openssl-sys": [openssl({ system })], }, }); };
Build a container.

Use the buildContainerImage function to build a container image. In this example, we build a container image with a simple python project. Container image builds with Tangram are fast, reproducible, and minimal.

import * as python from "tangram:python"; import { buildContainerImage } from "tangram:container"; export let build = () => { return buildContainerImage({ packages: [python()], command: "python", args: [Tangram.template`${import.meta.source}/main.py`], }); };
Pin and patch dependencies.

Tangram packages come with a lot of options for customization. In this example, we build the Zig compiler at a particular revision from GitHub and apply a patch from an unmerged pull request. Now every machine that uses this shell will have the same custom build of Zig.

import * as tg from "tangram:std/lib.ts"; import * as zig from "tangram:zig"; export let shell = () => { let zigSource = tg.fetchFromGitHub({ owner: "ziglang", repo: "zig", rev: "88d1258e08e668e620d5f8f4681315e555acbcd2", }); let zigPatch = new tg.Fetch({ url: "https://github.com/ziglang/zig/pull/9771.patch", hash: "c0bb3d56ee8f34fb82bd4d64375b565601c62d9c2a289991fc9f088758ad86f8", }); return tg.buildShell({ packages: [ zig({ source: zigSource, patches: [zigPatch], }), ], }); };
Try software without modifying your system.

Everything you build with Tangram is self contained and isolated from the rest of your system, so you can try software without affecting your other projects.

$ tg run ripgrep@13.0.0 -- --version ripgrep 13.0.0
Build and run on virtual machines.

Tangram has built-in virtualization so you can run builds and shells for other architectures and operating systems. Try the command below on macOS.

$ tg shell --system x86_64-linux -p coreutils -- uname -sm Linux x86_64