daspkg ships in the SDK as a prebuilt exe - the DAS_UTILS_SHIPPED_EXES entry in utils/CMakeLists.txt
(repo root); utils/REVIEW.das reads this line as the record of that decision.
Package manager for daslang. Installs, updates, builds, and manages daslang modules from git repositories or a central package index.
# run daspkg
daslang utils/daspkg/main.das -- <command> [args...]
# install a package by name (from the index)
daslang utils/daspkg/main.das -- install daspkg-test-pure
# install a package by URL
daslang utils/daspkg/main.das -- install github.com/borisbat/daspkg-test-pure
# install a specific version
daslang utils/daspkg/main.das -- install github.com/borisbat/daspkg-test-versions@1.0
# install all dependencies listed in .das_package
daslang utils/daspkg/main.das -- install
# install globally (shared across projects)
daslang utils/daspkg/main.das -- install --global dasImgui| Command | Description |
|---|---|
install <url|name|path>[@version] |
Install a package from git, index, or local path |
install (no args) |
Install all dependencies from .das_package |
remove <name> |
Remove an installed package |
update [name] |
Re-install at pinned version (re-clone, rebuild) |
upgrade [name] |
Upgrade to latest version |
list |
List installed packages |
search <query> |
Search the package index |
build |
Build all C/C++ packages (cmake) |
check |
Verify installed packages are present |
doctor |
Check environment (git, cmake, gh) |
release [--out <dir>] [--paranoid | --quick | --fat <class>] |
Bundle project as a redistributable standalone. A plain release mints the tune sidecar on the build box and ships it; --quick inherits a complete existing one instead of minting; --fat <class> builds a fat exe from the class profiles - no mint, no sidecar |
introduce [url] |
Submit a package to the index via PR |
withdraw <name> |
Remove a package from the index via PR |
update-index [--commit | --dry-run] |
Re-read every index entry's manifest from its repo and refresh the entry. Default opens a PR on the index repo; --commit pushes straight to its main branch; --dry-run prints the diff and changes nothing. A package whose clone or manifest fails is reported and exits 1 after the rest refreshed |
All package commands accept --global / -g to operate on global modules.
| Flag | Description |
|---|---|
--root <path> |
Project root (default: current directory) |
--force |
Force reinstall (overrides duplicate/version checks); also lets install, update and upgrade take a package whose manifest declares other platforms only |
--global, -g |
Operate on global modules in {das_root}/modules/ |
--color / --no-color |
Enable/disable ANSI colored output |
--verbose, -v |
Print debug details (git commands, resolve steps) |
--json |
Machine-readable JSON output (search, list, check) |
--branch <name>, -b <name> |
Install from a git branch (e.g. master) instead of a tag |
--out <path> |
Output directory for release (default: current directory) |
--wasm |
Target wasm64 (memory64, lowered to a 32-bit memory at link so every browser runs it): build --wasm builds the wasm64 runtime and module archives, release wasm ships compiled .wasm artifacts |
--wasm-lib-dir <path> |
Directory holding the wasm64 archives release wasm links (default <das_root>/web/output64/lib) |
--paranoid |
Accepted for compatibility; the tuner runs one margin-decided protocol and this flag no longer changes the budget |
--quick |
During release, accept a complete existing sidecar instead of re-minting (an incomplete or stale scope still mints - an exe never ships unmeasured). Forgetting it costs one re-mint, never correctness |
--commit |
During update-index, commit the refreshed index straight to the index repo's main branch (needs push access) |
--dry-run |
During update-index, print the refreshed packages.json as a diff and change nothing |
--fat <class> |
During release, build a fat exe for a CPU class (x86-avx2, x86-vnni512, x86-amx, arm-neon, arm-i8mm, ...): the plain code targets the class, every [tune] kernel ships one clone per class the library has a profile for, and the exe picks the clone from cpuid at startup. No mint and no shipped sidecar; a kernel with no profile entry for the class refuses the release. skills/tune.md, The fat exe |
Large packages (e.g. dasImgui) can be installed globally - once under {das_root}/modules/ - shared across all projects using that SDK. Avoids redundant clones and builds.
daspkg install --global dasImgui # install to das_root/modules/
daspkg list --global # list global packages
daspkg update --global dasImgui # re-install at pinned version
daspkg remove --global dasImgui # remove globally- Local install auto-uses global:
daspkg install foochecks the global lock file first. If compatible, records a reference instead of cloning. - Version mismatch: errors with a suggestion. Use
--forceto install locally, or--globalto update the global copy. - Shadow detection: if a module exists both locally and globally, the local version wins (warning printed). Removing the local copy falls back to global.
- CMake: global packages with native builds get a
.daspkg_standalonemarker so the main CMake skips them during auto-discovery.
Executable daslang script declaring metadata, version resolution, dependencies, and build info.
options gen2
require daslib/daspkg
[export]
def package() {
package_name("mymodule")
package_author("username")
package_description("What this module does")
package_source("github.com/user/mymodule")
package_license("MIT")
package_tag("networking")
package_min_sdk("0.4")
package_platform("windows") // optional; repeatable, or package_platforms(["windows", "wasm"]) - none declared = every platform
}
[export]
def resolve(sdk_version, version : string) {
if (version == "" || version == "latest") {
download_tag("v2.0")
} else {
download_tag("v{version}")
}
// alternatives: download_branch("main"), download_redirect("github.com/other/repo", "v3.0")
}
[export]
def dependencies(version : string) {
require_package("github.com/user/dep-a", ">=1.0")
require_package("github.com/user/dep-b")
}
[export]
def build() {
cmake_build() // or: custom_build("make all"), or: no_build()
}
[export]
def release() {
release_main("main.das")
release_include("assets/**") // release-owned; refreshed every time
release_include_if_missing("app.toml") // user-owned after initialization
release_include_tool("watchdog") // a built tool from bin/, shipped beside the exe
release_launcher("watchdog") // macOS: the .app opens this tool, which starts the exe
release_program("my-bench", "tools/bench.das") // a companion exe baked beside the main one, same class, shared runtime
}release_include_from(source, dest) copies an SDK-relative file into the bundle
on native and WASM releases. Omit dest to use the source filename. Both paths
use relative segments made of letters, digits, _, -, and .. The source can
use .. to reach a neighboring tree; destination paths reject .. segments.
A missing source or failed copy fails the release.
Use release_include_if_missing for editable deployment files. They are copied only when absent,
excluded from .daspkg_release.manifest, and preserved even when upgrading from an older manifest
that previously treated the same path as release-owned.
All functions except package() are optional. A repo without .das_package gets a "dumb clone" - no version resolution, no deps, no build.
- Shallow-clone from default branch
- Run
.das_packageresolve()-> checkout resolved tag/branch (or re-clone on redirect) - Move to
modules/<name>/ - Record in
daspkg.lock - Install transitive dependencies
- Auto-build if
.das_packagehasbuild()
my_project/
main.das
.das_package # project manifest (lists dependencies)
daspkg.lock # installed packages, versions, sources
modules/
<package_name>/
.das_module # provided by package author
.das_package # package manifest
_build/ # cmake build directory (if C++ package)
*.shared_module # built C++ output (if applicable)
.daspkg_cache/ # index cache (gitignored)
.daspkg_tmp/ # temp dir during install (gitignored)
{das_root}/ # daScript SDK root (for global modules)
modules/
.daspkg_global.lock # global lock file
<global_package>/
.daspkg_standalone # marker: built by daspkg, skip in CMake
{
"sdk_version": "",
"packages": [
{
"name": "mymodule",
"source": "github.com/user/mymodule",
"version": "1.0",
"tag": "v1.0",
"branch": "",
"root": true,
"local": false,
"global": false
}
]
}- root -
trueif user-installed,falseif transitive dependency - tag/branch - resolved git ref
- local - installed from local path
- global - resolved from global install (no local copy)
The global lock file ({das_root}/modules/.daspkg_global.lock) uses the same format.
| File | Description |
|---|---|
main.das |
CLI entry point - parses args, dispatches to commands |
commands.das |
Command implementations: install, remove, update, upgrade, build, check, doctor |
index.das |
Package index: fetch, search, introduce, withdraw |
lockfile.das |
LockFile / PackageEntry structs, JSON serialization |
package_runner.das |
In-process .das_package compiler - compiles, simulates, extracts metadata |
utils.das |
Shared utilities: run_cmd, force_rmdir, path helpers |
daslib/daspkg.das |
API module that .das_package scripts require |
The package runner compiles .das_package scripts in-process using compile_file + simulate + invoke_in_context. It calls exported functions and reads state from daslib/daspkg module globals via get_context_global_variable.
A manifest's package_platform list is a declaration, not a build probe: it names the
get_platform_name() values (windows, darwin, linux, emscripten; wasm and macos are
accepted spellings) the author supports, and an empty list means every platform. install
reads it from the cloned checkout before anything lands in modules/ and refuses a host outside
the list with exit code 3 (RC_UNSUPPORTED_PLATFORM), the same for a dependency on the chain;
--force installs anyway with a warning, on update and upgrade too. release checks the
root package against the host and release wasm against emscripten. The index carries the
list as platforms, so search shows it and the nightly index sweep (index_sweep.cmake)
skips an entry whose list leaves out the sweep host instead of failing on it; an install that
exits 3 under the sweep is also a skip, flagged as a stale index entry until the next
update-index. A name outside the vocabulary fails the manifest outright, under --force too.
The web build pins every archive it produces - the runtime and each module - into its own
output directory, web/output64/lib (das_pin_wasm_archives in web/CMakeLists.txt), and
build --wasm stages from there alone. <das_root>/lib belongs to the desktop build and is
never read: an archive found there is stale by construction - a wasm archive an earlier web
configure wrote, or a native one - and staging it over the fresh build ships old code under the
new toolchain id. Staging still identifies each archive by content - it walks the archive's
members to the first object and asks for the wasm magic - and refuses a native one, so a web
build directory configured for another target is caught before release wasm fails to link.
release wasm stamps the page it releases. Where the shell's markup carries the
__DAS_BUILD_STAMP__ placeholder, wherever the shell puts it, the release replaces it with the
source revision (git describe --always --dirty=+ --abbrev=9, or unknown outside a git tree)
and the build time with its zone, after the external files have landed. The shells this
repository ships put the placeholder in a das-build meta tag in the page head, so the stamp
draws nothing and a tester reads which build answered from the page source, or from
document.querySelector('meta[name=das-build]').content in the console. A shell whose markup
lacks the placeholder ships unstamped, and so does a page the release cannot read; the release
is silent about both.
Review gates live in REVIEW.md - run the unit suite on every change.
| File | Count | Type |
|---|---|---|
test_daspkg.das |
151 | Unit tests (local operations, parsing, lock file, package_runner) |
test_daspkg_git.das |
70 | Integration tests (git clone, version resolve, index, global modules) |
# unit tests - fast, no network
daslang dastest/dastest.das -- --test utils/daspkg/test_daspkg.das
# integration tests - requires network
daslang dastest/dastest.das -- --test utils/daspkg/test_daspkg_git.das| Repository | Purpose |
|---|---|
borisbat/daspkg-test-pure |
Pure daslang module |
borisbat/daspkg-test-versions |
Module with version tags (v1.0, v2.0) |
borisbat/daspkg-test-deps |
Module with transitive dependency |
borisbat/daspkg-index |
Central package index |
- Packages are git repositories - no centralized registry server to maintain
- Version = git tag.
resolve()maps version requests to tags. - Index is a curated JSON file in a git repo. Authors add via PR (
daspkg introduce).
- Authors can put version-conditional logic in
resolve()anddependencies() - Same pattern as
.das_module- familiar to daslang authors - daspkg provides registration functions via
daslib/daspkg;.das_packagejust calls them
- Game projects need reproducible builds -
modules/is self-contained - Global install (
--global) for large shared modules (dasImgui, etc.) - Runtime already scans both
das_root/modules/and projectmodules/
- Package version - semver, resolved via
.das_packageresolve() - SDK version -
resolve()receives it, can return different tags per SDK - Dependency constraints - operators
>=,>,<=,<,=, comma AND:">=1.0,<2.0" - Diamond deps - first installed wins (single
requirenamespace). Manual upgrade with--force.
- Only external dependency:
gitCLI. No HTTP library needed. - Shallow clones (
--depth 1) for speed. - Local installs: filesystem copy, no git.
- git - required for all remote operations
- cmake - required for building C/C++ packages
- gh (GitHub CLI) - optional, only for
introduce/withdraw
Run daspkg doctor to check your environment.
For release wasm, release_include_symbols() enables --jit-debug --jit-stack
during cross-compilation and -g3 --emit-symbol-map during linking. The deployed
WASM retains generated debug information and function names, with an adjacent
.html.symbols map. Prebuilt archives contribute only their existing debug data.
Runtime assertions and heap instrumentation remain explicit release_emcc_arg
choices, so requesting symbols alone does not silently enable those checks.
With emsdk active, node utils/daspkg/test_wasm_external.cjs checks external-file
staging and failure propagation through real WASM releases. Set DASLANG_BIN and
DASLANG_WASM_LIB to override the local compiler and web/output64/lib defaults.