Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ src/das/**/*.h binary
utils/dasFormatter/ds_parser.cpp binary
utils/dasFormatter/ds_parser.output binary
daslib/_aot_generated/*.cpp binary

# Shell scripts must keep LF endings even when checked out under autocrlf=true.
*.sh text eol=lf
33 changes: 33 additions & 0 deletions .github/workflows/extended_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,39 @@ jobs:
cmake --build ./build --config Release --target all_utils_exe
$BIN/daslang -exe -output ./bin/das-fmt ./utils/das-fmt/dasfmt.das

- name: "Sequence release smoke test"
# Full daspkg install -> release -> launch cycle on the in-tree sequence
# game. Exercises release_include_dll + the loader fixes (Windows
# LoadLibraryEx, POSIX rpath $ORIGIN / @loader_path) end-to-end.
# Lives in extended_checks (not build.yml) because sequence transitively
# requires dasHV, which is DAS_HV_DISABLED=ON by default and only flipped
# ON via ci/release_modules.txt in this workflow.
run: |
set -eux
# The main extended_checks build only targets daslang/das-fmt/daslang_static.
# Sequence loads dasGlfw + dasLiveHost + dasHV + dasAudio + dasPUGIXML +
# dasStbImage at runtime; build their dynamic-module targets first.
# No `cmake --install` -- the source tree already IS a usable daslang
# root after build (bin/daslang via EXECUTABLE_OUTPUT_PATH; modules/<X>/
# .shared_module via ADD_DAS_SHARED_MODULE_LIB output dirs; utils/daspkg
# lives in the source tree). cmake --install would also try to install
# daslang-live which extended_checks doesn't build.
cmake --build ./build --config Release --target \
dasModuleGlfw dasModuleLiveHost dasModuleHV dasModuleAudio \
dasModulePUGIXML dasModuleStbImage --parallel
case "${{ matrix.target }}" in
windows)
pwsh examples/games/sequence/ci_smoke_test.ps1 "$(pwd)"
;;
linux)
sudo apt-get install -y xvfb
xvfb-run -a bash examples/games/sequence/ci_smoke_test.sh "$(pwd)"
;;
*)
bash examples/games/sequence/ci_smoke_test.sh "$(pwd)"
;;
esac

- name: "Run formatter"
run: |
set -eux
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ build-rwdi/
build-asan/
build-ubsan/
build-linux-asan/
build-linux/
# All build binaries stored here
bin/
# JIT stores dll's here
Expand All @@ -26,6 +27,8 @@ _aot_generated/
.vscode/
.cache/

opencoede.json

modules/dasSFML/libsfml/
site/
doc/sphinx-build/
Expand Down Expand Up @@ -98,3 +101,4 @@ modules/dasStdDlg/dasModuleStdDlg_debug.pdb
modules/dasUnitTest/dasModuleUnitTest_debug.pdb
build-ubsan/.ninja_log
doc/sphinx-build-latex/environment.pickle
opencode.json
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Task-specific instructions are split into skill files under `skills/`. You MUST
| `skills/clargs_migration.md` | Editing any tool that still parses `get_command_line_arguments()` directly — migrate to `daslib/clargs` in the same PR |
| `skills/json.md` | Reading/writing JSON in `.das` code (`sprint_json`/`sscan_json`, `JV`, manual `JsonValue?`) |
| `skills/xml.md` | XML via `dasPUGIXML`/`PUGIXML_boost` (RAII parsing, builder, XPath, struct round-trip) |
| `skills/sql.md` | SQL via `dasSQLITE` — `[sql_table]` / `[sql_view]` / `[sql_fts5]` / `[sql_function]`, the `_sql(...)` LINQ-to-SQL flagship + `_each_sql` / `_sql_update` / `_sql_delete` / `_sql_upsert` / `_create_view`, custom-type adapter rail, `@sql_json` / `@sql_blob` columns, transactions, migrations (`[sql_migration]` + `with_latest_sqlite`) |
| `skills/filesystem.md` | Any `.das` path/filename/filesystem op — must use `fio` helpers, never `rfind`/`slice` |
| `skills/detect_dupe.md` | Duplicate-function detection (corpus build, MCP tools `export_corpus`/`detect_duplicates`, CLI under `utils/detect-dupe/`) |
| `skills/find_dupe.md` | AI-judging a detect-dupe report via Claude (MCP tools `judge_duplicates`/`find_dupe`, CLI under `utils/find-dupe/`); cost guardrails (`--dry-run`, `--max-clusters`, `--positives-only`) |
Expand Down
20 changes: 20 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,26 @@ MACRO(ADD_DAS_SHARED_MODULE_LIB module_name)
)
# Debug builds use _debug suffix so Debug and Release .shared_module can coexist
SET_PROPERTY(TARGET ${module_name} PROPERTY OUTPUT_NAME_DEBUG "${module_name}_debug")
# Make the .shared_module's own directory the first place its native deps are
# searched at load time (e.g. modules/dasGlfw/libglfw.so.3 next to
# modules/dasGlfw/dasModuleGlfw.shared_module). On Windows the equivalent is
# done at runtime via LoadLibraryEx flags in src/misc/sysos.cpp. Use APPEND
# so per-module CMakeLists.txt that adds extra rpaths doesn't clobber this.
if(APPLE)
SET_PROPERTY(TARGET ${module_name} APPEND PROPERTY
BUILD_RPATH "@loader_path"
)
SET_PROPERTY(TARGET ${module_name} APPEND PROPERTY
INSTALL_RPATH "@loader_path"
)
elseif(UNIX)
SET_PROPERTY(TARGET ${module_name} APPEND PROPERTY
BUILD_RPATH "\$ORIGIN"
)
SET_PROPERTY(TARGET ${module_name} APPEND PROPERTY
INSTALL_RPATH "\$ORIGIN"
)
endif()
# Ignore tutorial, profile, test.
if(NOT "${FOLDER_NAME}" STREQUAL "tutorial" AND
NOT "${FOLDER_NAME}" STREQUAL "profile" AND
Expand Down
12 changes: 12 additions & 0 deletions daslib/daspkg.das
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ struct ReleaseSpec {
include_globs : array<string> //!< asset globs to ship
exclude_globs : array<string> //!< asset globs to skip
forced_modules : array<string> //!< force-include shared module by das_name (auto-detection misses media-only or runtime-loaded packages)
native_dlls : array<string> //!< native shared libraries to ship next to this package's .shared_module (e.g. "glfw3.dll" / "libglfw.so.3.4"); resolved against <das_root>/bin/ (Windows) or <das_root>/lib/ (POSIX)
bundle_id : string //!< macOS .app CFBundleIdentifier (reverse-DNS); defaults to com.<author>.<name>
}

Expand All @@ -164,6 +165,17 @@ def release_shared_module(name : string) {
_release_spec.forced_modules |> push(name)
}

//! Declare a native shared library that must be shipped next to this package's
//! `.shared_module` in a release bundle. `name` is the platform-specific filename
//! (e.g. `"glfw3.dll"` on Windows, `"libglfw.so.3.4"` on Linux,
//! `"libglfw.3.4.dylib"` on macOS). daspkg searches `<das_root>/bin/` on Windows
//! and `<das_root>/lib/` on POSIX, then copies the file into
//! `<bundle>/modules/<package>/`. Errors loudly if the file is not found —
//! authors should guard per-platform with `if (get_platform_name() == "windows")`.
def release_include_dll(name : string) {
_release_spec.native_dlls |> push(name)
}

def release_bundle_id(id : string) {
_release_spec.bundle_id = id
}
Expand Down
125 changes: 125 additions & 0 deletions examples/games/sequence/ci_smoke_test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env pwsh
# CI smoke test: full daspkg install -> release -> launch cycle for the sequence
# game on Windows. Exercises the release_include_dll manifest API + daspkg's
# native-dep shipping + the LoadLibraryEx-based loader resolution end-to-end.
#
# Args: $args[0] (optional) - path to a daslang install root (must contain
# bin/daslang.exe and utils/daspkg/main.das). Defaults to env DAS_ROOT.
#
# Exit: 0 on success; non-zero on any step failure.

$ErrorActionPreference = "Stop"

$dasRoot = if ($args.Count -ge 1) { $args[0] } elseif ($env:DAS_ROOT) { $env:DAS_ROOT } else { "" }
if (-not $dasRoot) {
Write-Error "usage: ci_smoke_test.ps1 <daslang-install-root> (or set DAS_ROOT)"
exit 2
}
$dasRoot = (Resolve-Path $dasRoot).Path
# Honor $env:BIN if set (extended_checks sets it to bin/Release for Visual
# Studio multi-config generators); default to <dasRoot>/bin for installed layouts.
$binDir = if ($env:BIN) { (Resolve-Path $env:BIN).Path } else { Join-Path $dasRoot "bin" }
$daslang = Join-Path $binDir "daslang.exe"
$daspkg = Join-Path $dasRoot "utils\daspkg\main.das"
foreach ($p in @($daslang, $daspkg)) {
if (-not (Test-Path $p)) { Write-Error "missing: $p"; exit 2 }
}

# Source dir of THIS script's project (the in-tree sequence example).
$srcDir = Split-Path -Parent $PSCommandPath
$workDir = Join-Path $env:TEMP "daspkg_smoke_sequence_$([guid]::NewGuid().ToString('N').Substring(0,8))"
Write-Host "smoke: src=$srcDir"
Write-Host "smoke: work=$workDir"

# Copy the project to a scratch dir so we don't dirty the source tree on rerun.
# Filter out previous bundle/install artifacts so we always start clean.
New-Item -ItemType Directory -Path $workDir | Out-Null
Get-ChildItem -Path $srcDir -Force | Where-Object {
$_.Name -notin @("modules", "sequence", "daspkg.lock", ".daspkg_cache", ".daspkg_tmp", ".daspkg.log")
} | ForEach-Object {
Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $workDir $_.Name) -Recurse -Force
}

try {
Push-Location $workDir
Write-Host "smoke: daspkg install"
& $daslang $daspkg -- install
if ($LASTEXITCODE -ne 0) { Write-Error "daspkg install failed (rc=$LASTEXITCODE)"; exit 1 }

Write-Host "smoke: daspkg release"
& $daslang $daspkg -- release
if ($LASTEXITCODE -ne 0) { Write-Error "daspkg release failed (rc=$LASTEXITCODE)"; exit 1 }

# Verify expected bundle artifacts. Fail the test if anything is missing -
# these are the load-bearing files for a runnable bundle.
$bundle = Join-Path $workDir "sequence"
$required = @(
(Join-Path $bundle "sequence.exe"),
(Join-Path $bundle "libDaScriptDyn.dll"),
(Join-Path $bundle "libDaScriptDyn_runtime.dll"),
(Join-Path $bundle "modules\dasGlfw\dasModuleGlfw.shared_module"),
# The whole point: native dep colocated with its shared_module.
(Join-Path $bundle "modules\dasGlfw\glfw3.dll"),
# Regression check: dep release_include assets still ship.
(Join-Path $bundle "modules\das-cards\cards\svg-cards.svg")
)
foreach ($f in $required) {
if (-not (Test-Path $f)) {
Write-Error "missing bundle artifact: $f"
exit 1
}
Write-Host "smoke: ok $f"
}

# Launch the bundled exe with --smoke under a 60s outer timeout. --smoke
# exits cleanly after module load + arg parse, before glfwCreateWindow.
# windows-latest-fat runners are headless (no interactive WindowServer
# session), so a full GLFW init crashes the same way macOS arm64-xlarge
# does. --smoke still exercises the complete loader chain (every `require`
# resolves its .shared_module and dlopens its native deps at module-load
# time, before main() runs), which is the actual change under test.
# Failure modes: missing DLL -> non-zero exit immediately; hang -> timeout.
# Use System.Diagnostics.Process directly because Start-Process -PassThru +
# -RedirectStandard* drops ExitCode (known PowerShell quirk).
Write-Host "smoke: launching bundled sequence.exe --smoke"
$exe = Join-Path $bundle "sequence.exe"
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = $exe
$psi.Arguments = "--smoke"
$psi.WorkingDirectory = $bundle
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$psi.UseShellExecute = $false
$proc = [System.Diagnostics.Process]::Start($psi)
# Async-drain streams to prevent buffer-fill deadlocks.
$outTask = $proc.StandardOutput.ReadToEndAsync()
$errTask = $proc.StandardError.ReadToEndAsync()
$exited = $proc.WaitForExit(60000)
if (-not $exited) {
Write-Host "smoke: TIMEOUT (60s) - sequence did not exit; killing"
$proc.Kill()
$proc.WaitForExit()
$outTask.Wait()
$errTask.Wait()
Write-Host "--- stdout ---"; Write-Host $outTask.Result
Write-Host "--- stderr ---"; Write-Host $errTask.Result
Write-Error "smoke: outer timeout fired"
exit 1
}
# Block until WaitForExit confirms streams are flushed (idiomatic .NET pattern).
$proc.WaitForExit()
$outTask.Wait()
$errTask.Wait()
Write-Host "--- stdout ---"; Write-Host $outTask.Result
Write-Host "--- stderr ---"; Write-Host $errTask.Result
$rc = $proc.ExitCode
if ($rc -ne 0) {
Write-Error "smoke: sequence exited with rc=$rc"
exit 1
}
Write-Host "smoke: PASS (rc=0)"
} finally {
Pop-Location
# Leave the work dir on disk for postmortem; CI runners get cleaned anyway.
Write-Host "smoke: artifacts in $workDir"
}
Loading
Loading