Skip to content

bnlang/bpm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bpm — Bnlang Package Manager

License: MIT

bpm is the official package manager for the Bnlang programming language. It installs and publishes both pure-bnl libraries and native plugins (.dll / .so / .dylib), backed by a versioned registry with integrity-checked downloads.

  • Single binary — Go-based, no runtime dependencies.
  • Reproducible — every install writes a bnl.lock that pins direct + transitive deps to a concrete version plus a SHA-256 integrity hash.
  • Semver resolution^1.2.3, ~1.2, >=1.0 <2, and exact 1.2.3 all work.
  • Native plugin friendly — publish a single source package; resolves per-platform on install (windows-x64, linux-x64, darwin-arm64, …).
  • Tokens for CI — long-lived bearer tokens scoped per machine, listable and revocable from the CLI.

Install

From a release

Download the latest bpm binary for your platform from the Bnlang releases page and put it on your PATH.

Build from source

Requires Go 1.23 or newer.

git clone https://github.com/bnlang/bpm.git
cd bpm
go build -o bpm        # produces ./bpm on Linux/macOS, .\bpm.exe on Windows

Verify:

bpm --help

Cross-build for all platforms

bpm is pure Go (no cgo), so cross-compilation works from any host. The repo ships scripts that produce stripped, version-stamped binaries for every supported platform — each one packed into a .zip archive with a single bpm (or bpm.exe) executable at the root:

# Linux / macOS
./build.sh                  # all six targets
./build.sh windows-x64      # one target only

# Windows
.\build.ps1
.\build.ps1 windows-x64

Output layout matches the platform strings bpm uses elsewhere:

Target Output Archive contents
windows-x64 dist/bpm-windows-x64.zip bpm.exe
windows-x86 dist/bpm-windows-x86.zip bpm.exe
linux-x64 dist/bpm-linux-x64.zip bpm
linux-arm64 dist/bpm-linux-arm64.zip bpm
darwin-x64 dist/bpm-darwin-x64.zip (Intel Macs) bpm
darwin-arm64 dist/bpm-darwin-arm64.zip (Apple Silicon) bpm

Each archive is ~3.3–3.7 MB compressed (~8–9 MB uncompressed). Users download one zip, unpack it, drop the binary onto PATH, done.

Override the version stamp with VERSION=v1.1.0 ./build.sh (or $env:VERSION = "v1.1.0"; .\build.ps1) when cutting a release. The scripts use CGO_ENABLED=0, -trimpath, and -ldflags="-s -w" so the resulting binaries are static.

build.sh requires the zip command (preinstalled on most Linux/macOS hosts; apt install zip / brew install zip if missing). build.ps1 uses the built-in Compress-Archive, no extra tooling required.

For a single ad-hoc build with no packaging, the underlying command is:

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bpm .

Quick start

# 1. (Optional) Point bpm at a different registry. The default is the
#    official one at https://bpm.bnlang.dev — set BPM_REGISTRY to override.
export BPM_REGISTRY=https://bpm.bnlang.dev

# 2. Authenticate (interactive — email + password, stored as a bearer token
#    under ~/.bnl/auth.json with mode 0600).
bpm login

# 3. Inside any directory:
bpm init                          # write a starter bnl.json

# 4. Add and install a dependency.
bpm install some-package
bpm install some-package@1.4.0    # pinned exact version
bpm install -g some-package       # install globally under ~/.bnl/deps/

# 5. Use it from bnl code.
#    src/main.bnl:
#        import "some-package" as pkg;
#        print(pkg.hello());

bnl src/main.bnl

To install everything declared in an existing project's bnl.json (e.g. on a fresh checkout):

bpm install

Commands

Command Purpose
bpm init Create a starter bnl.json in the current directory.
bpm install Install every dependency listed in bnl.json.
bpm install <name>[@version] Add and install one package; pin into bnl.json.
bpm install -g <name> Install globally under ~/.bnl/deps/<name>/.
bpm uninstall <name> [-g] Remove an installed dependency.
bpm list [-g] Show installed packages with their versions.
bpm signup Create an account on the active registry.
bpm login Authenticate with the registry. Prompts for email + password.
bpm logout Forget the stored token for the active registry.
bpm publish Publish the current package. Auto-detects pure-bnl vs. native.
bpm publish --platform <os-arch> --binary <path> Publish a single native-plugin platform manually.
bpm token create --label <label> Mint a CI token scoped to a label.
bpm token list List your active tokens (id + label only, never the secret).
bpm token revoke <id> Revoke a token.

Global flags accepted by every command:

  • --registry <url> — override the registry for this invocation (overrides BPM_REGISTRY).
  • -q, --quiet — suppress non-error output.

bpm install flags

  • -g, --global — install into ~/.bnl/deps/ instead of ./deps/.
  • -f, --force — reinstall registry packages even if deps/<name>/bnl.json already reports the requested version. By default a no-op bpm install skips already-installed packages and prints → name@version (cached); --force re-downloads and re-unpacks every one. Local file: dependencies are always re-unpacked regardless.
  • --ignore-failed — keep going when an individual package fails. The failing package is skipped (no bnl.json / bnl.lock entry written for it) and a summary of skipped packages is printed at the end. The default is strict — any failure aborts the whole install.
  • --platforms <list> — comma-separated platforms the dep applies to (e.g. windows-x64,darwin-arm64,darwin-x64). Writes the dep as a scoped object in bnl.json (see Platform-scoped dependencies below). On any platform outside the list the dep is ignored — not installed, not in the lockfile, no warning. On listed platforms it remains required, so a missing registry asset is still a hard error.
  • -O, --optional — tolerate install failure (no version match, no asset for this platform, network error) with a warning instead of aborting. Writes the dep as a scoped object with "optional": true. Use this for best-effort deps where you actively want to be told it didn't install but don't want it to break the build. Pair with --platforms to be both scoped and lenient inside the scope.

The bnl.json manifest

{
    "name": "mathx",
    "version": "1.2.0",
    "description": "Vector and matrix helpers.",
    "license": "MIT",
    "homepage": "https://github.com/example/mathx",
    "repository": "https://github.com/example/mathx",
    "main": "src/index.bnl",
    "dependencies": {
        "core-utils": "^1.1.0",
        "logger":     "~0.4"
    }
}

Required: name. Everything else is optional; only name, main, and native are read by the Bnlang runtime itself — bpm uses the rest for the registry.

Platform-scoped dependencies

A dependency value can be a plain version string or an object that scopes the dep to specific platforms and/or marks it as best-effort:

{
    "dependencies": {
        "torch-bnlang": "^1.1.0",

        "onnxruntime-genai-bnlang": {
            "version":   "^0.1.0",
            "platforms": ["windows-x64", "darwin-arm64", "darwin-x64"]
        },

        "fast-tokenizer-bnlang": {
            "version":  "^0.3.0",
            "optional": true
        }
    }
}

Two independent fields:

  • platformswhere this dep applies. Outside the listed platforms the dep is silently ignored: not resolved, not installed, not in the lockfile. On listed platforms it's required as normal.
  • optionalwhether failure is tolerable. When true, an install failure (no asset for this platform, no version satisfying the spec, network error) becomes a warning instead of aborting. Both fields can be combined.
platforms set? current platform listed? optional? behavior
no no required everywhere (default)
no yes tried everywhere; warn-skip on failure
yes yes no required here; hard error on failure
yes yes yes tried here; warn-skip on failure
yes no not applicable, silent

Use --platforms / -O on bpm install <name> to write either field via the CLI — no need to hand-edit bnl.json.

Native plugins — targets

When publishing a C/C++ FFI plugin, declare the freshly-built binary path per platform. bpm picks the right one at install time and renames it to a canonical filename:

{
    "name": "mathx-plugin",
    "version": "0.3.1",
    "targets": {
        "windows-x64":   "build/windows/mathx.dll",
        "linux-x64":     "build/linux/mathx.so",
        "darwin-arm64":  "build/darwin/mathx.dylib"
    }
}

Every other regular file in the binary's directory is shipped alongside (sqlite3.dll, libssl.dylib, etc.). Use a .bpmignore file (gitignore syntax) to exclude .pdb, .ilk, intermediate build artifacts, and similar noise.

Supported platform strings: windows-x64, windows-x86, linux-x64, linux-arm64, darwin-x64, darwin-arm64.

The installed manifest (under deps/<name>/bnl.json) replaces targets with a single native: "<canonical>" entry — the runtime looks at exactly that field.


The bnl.lock lockfile

bpm install generates bnl.lock next to bnl.json. It pins every direct and transitive dependency to a concrete version plus a SHA-256 hash of the package tarball:

{
    "packages": {
        "logger": {
            "version":   "0.4.7",
            "integrity": "sha256-abcd1234…",
            "deps":      { "core-utils": "^1.1.0" }
        }
    }
}

Commit both bnl.json and bnl.lock. The lockfile is what makes someone else's bpm install reproduce yours; the manifest alone resolves to latest matching, which drifts over time.


Authentication

bpm login stores a bearer token in ~/.bnl/auth.json (mode 0600), keyed by registry URL:

{
    "https://bpm.bnlang.dev": {
        "token": "bpm_…",
        "email": "you@example.com"
    }
}

For CI / publish automation, mint a dedicated token instead of using your password-derived session token:

bpm token create --label github-actions
#   bpm_eyJhbGciOi…                ← copy this, store as a CI secret

# In CI:
export BPM_TOKEN=bpm_eyJhbGciOi…
bpm publish

bpm token list and bpm token revoke <id> round out the lifecycle.


Files bpm touches

Path Purpose
./bnl.json Project manifest.
./bnl.lock Generated lockfile. Commit it.
./deps/<name>/ Project-local installed packages.
~/.bnl/deps/<name>/ Global installs (from bpm install -g).
~/.bnl/auth.json Registry bearer tokens. chmod 600.
./.bpmignore gitignore-syntax exclude list at publish time.

bpm never writes outside these paths.


Environment variables

  • BPM_REGISTRY — default registry URL (overridden per command by --registry).
  • BPM_TOKEN — bearer token used for the current invocation. Takes precedence over ~/.bnl/auth.json. Useful for CI.

Project layout

bpm/
├── cmd/             # cobra command handlers (one file per top-level subcommand)
├── internal/
│   ├── archive/     # tarball creation + extraction
│   ├── auth/        # ~/.bnl/auth.json read/write
│   ├── lockfile/    # bnl.lock read/write
│   ├── manifest/    # bnl.json read/write
│   ├── paths/       # ~/.bnl/, deps/, …
│   ├── platform/    # windows-x64 / linux-x64 / darwin-arm64 / …
│   ├── registry/    # HTTP client for the registry API
│   ├── resolver/    # semver constraint resolution
│   └── semvr/       # thin wrapper around Masterminds/semver
├── main.go
├── go.mod
└── README.md

Contributing

Pull requests welcome. For larger changes, please open an issue first to discuss the approach.

  • Stick to the existing Go style (gofmt, goimports).
  • Each subcommand has its own file under cmd/; new commands follow the same pattern.
  • New internal/ packages should have a doc.go-style comment at the top explaining the package's role.

License

MIT — see LICENSE.

Copyright © 2025 Bnlang | Mamun

About

BPM is the official package manager for the Bangla programming language (Bnlang). It installs and publishes both pure-bnl libraries and native plugins (`.dll` / `.so` / `.dylib`), backed by a versioned registry with integrity-checked downloads.

Topics

Resources

License

Stars

Watchers

Forks

Contributors