feat(pack): .packignore, so a pack root can leave build artefacts out
A pack root is packed wholesale, and that is the right default - a game writes `pack "assets"` and everything it opens is in the pack. What also goes in is everything the game does NOT open: the preview renders a model pipeline leaves beside its meshes, the intermediate a texture bake writes and never reads again, the .blend the .gltf came out of. Nothing errors, nothing looks wrong, and the app is simply bigger than the game. The only way out the manifest offered was naming every file by hand, which is worse - a list that goes stale the day someone adds a texture. So `.packignore`, with gitignore's rules, because that is the file everyone already knows. Anchored and floating patterns, `preview/` for directories only, `*` and `?` stopping at a separator where `**` crosses one, `[a-z]` classes, `!` re-includes with the last line winning, a deeper file beating a shallower one, and no re-including out of an ignored directory. The semantics are not claimed, they are checked: the implementation was diffed against git itself over two fixtures - 35 paths, 19 patterns, nested ignore files, directory negation, `[!0-9]` and `\#` escaping - and `git check-ignore` and `ludic pack` agree on every path. Two rules of its own, because a pack is not a working tree. `.packignore` is never packed (nothing reads one at run time, and --no-ignore does not bring it back). And it governs the project's own roots only: a package's resources - the renderer's shaders above all - are added after the gather, so a stray `*.frag` in a game's ignore file cannot quietly un-ship what it needs to draw anything. `ludic pack` reports what it left out; `--no-ignore` packs everything so you can see what a rule is costing. `ludic bundle` gathers through the same path, so the two agree by construction.
This commit is contained in:
parent
841ae1d442
commit
cf814d4a97
6 changed files with 484 additions and 5 deletions
|
|
@ -157,6 +157,70 @@ function pack_probe_src() -> pointer {
|
|||
return s
|
||||
}
|
||||
|
||||
# `.packignore` — the pack's exclude file, with gitignore's semantics. The feature it
|
||||
# exists for is that a pack root is packed wholesale: the preview renders, bake
|
||||
# intermediates and .blend files a model pipeline leaves beside its meshes all shipped,
|
||||
# and the only alternative the manifest offered was naming every file by hand.
|
||||
#
|
||||
# The patterns below are one of each kind that people actually rely on, and the assert
|
||||
# is the exact set that comes out - both halves matter, because a rule that excludes too
|
||||
# much is as bad as one that excludes nothing and is just as quiet.
|
||||
function packignore_case() -> void {
|
||||
let lbl = "ludic pack -> .packignore excludes, with gitignore's rules"
|
||||
let work = `{tmp_dir()}/packign`
|
||||
let root = capture_line("pwd")
|
||||
run(`rm -rf {work}`)
|
||||
run(`mkdir -p {work}/assets/kit/preview {work}/assets/deep/a/tmp {work}/assets/scratch {work}/assets/sub/scratch {work}/assets/keep`)
|
||||
write_file(`{work}/assets/kit/preview/x.png`, "x") # preview/ - a directory, any depth
|
||||
write_file(`{work}/assets/kit/model.blend`, "x") # *.blend - a name, any depth
|
||||
write_file(`{work}/assets/kit/hiker.png`, "x") # kept
|
||||
write_file(`{work}/assets/deep/a/tmp/junk.txt`, "x") # **/tmp
|
||||
write_file(`{work}/assets/deep/a/keep.txt`, "x") # kept
|
||||
write_file(`{work}/assets/scratch/s.txt`, "x") # /assets/scratch - anchored
|
||||
write_file(`{work}/assets/sub/scratch/keep.txt`, "x") # kept: the anchor does not reach here
|
||||
write_file(`{work}/assets/keep/other.png`, "x") # assets/keep/*.png
|
||||
write_file(`{work}/assets/keep/important.png`, "x") # kept: re-included by !
|
||||
write_file(`{work}/assets/preview`, "x") # kept: a FILE, and the rule says directory
|
||||
var ig = "# what nobody opens" + nl()
|
||||
ig = ig + "preview/" + nl() + "*.blend" + nl() + "/assets/scratch" + nl() + "**/tmp" + nl()
|
||||
ig = ig + "assets/keep/*.png" + nl() + "!assets/keep/important.png" + nl()
|
||||
write_file(`{work}/.packignore`, ig)
|
||||
|
||||
if not shq(`cd {work} && {root}/bin/ludic pack --out o.lpak assets > pack.out 2>&1`) {
|
||||
bad2(lbl, capture_line(`tail -1 {work}/pack.out`)); return
|
||||
}
|
||||
# cut, not awk: `--list` prints "<size>\t<name>" and awk's braces collide with string
|
||||
# interpolation. capture_line joins the lines with spaces, which is the comparison.
|
||||
let got = capture_line(`cd {work} && {root}/bin/ludic pack --list o.lpak | tail -n +2 | cut -f2 | LC_ALL=C sort`)
|
||||
let want = "assets/deep/a/keep.txt assets/keep/important.png assets/kit/hiker.png assets/preview assets/sub/scratch/keep.txt"
|
||||
if got != want {
|
||||
bad2(lbl, `packed [{got}], wanted [{want}]`); return
|
||||
}
|
||||
|
||||
# a deeper .packignore wins over a shallower one, which is what makes a kit able to
|
||||
# keep something the project excludes everywhere else
|
||||
write_file(`{work}/assets/kit/.packignore`, "!*.blend" + nl())
|
||||
if not shq(`cd {work} && {root}/bin/ludic pack --out o2.lpak assets > pack2.out 2>&1`) {
|
||||
bad2(lbl, capture_line(`tail -1 {work}/pack2.out`)); return
|
||||
}
|
||||
if not shq(`cd {work} && {root}/bin/ludic pack --list o2.lpak | grep -q 'assets/kit/model.blend'`) {
|
||||
bad2(lbl, "a nested !*.blend did not win over the root rule"); return
|
||||
}
|
||||
# and an ignore file is never itself an asset, whatever the rules say
|
||||
if shq(`cd {work} && {root}/bin/ludic pack --list o2.lpak | grep -q packignore`) {
|
||||
bad2(lbl, ".packignore was packed"); return
|
||||
}
|
||||
# --no-ignore is the escape hatch: everything, minus the control files
|
||||
if not shq(`cd {work} && {root}/bin/ludic pack --no-ignore --out o3.lpak assets > pack3.out 2>&1`) {
|
||||
bad2(lbl, capture_line(`tail -1 {work}/pack3.out`)); return
|
||||
}
|
||||
let n3 = capture_line(`cd {work} && {root}/bin/ludic pack --list o3.lpak | tail -n +2 | wc -l | tr -d ' '`)
|
||||
if n3 != "10" {
|
||||
bad2(lbl, `--no-ignore packed {n3} files, wanted 10`); return
|
||||
}
|
||||
ok(lbl)
|
||||
}
|
||||
|
||||
# `ludic bundle` has to produce something macOS actually accepts as an
|
||||
# application, not a directory that looks like one: a plist that plutil parses,
|
||||
# an executable where Info.plist says it is, an .icns, and a signature. The
|
||||
|
|
@ -608,6 +672,7 @@ function cmd_dev_test() -> int {
|
|||
install_layout_case()
|
||||
scaffold_names_case()
|
||||
pack_roundtrip_case()
|
||||
packignore_case()
|
||||
bundle_case()
|
||||
|
||||
# install.sh is what the landing page tells people to pipe into sh, and it is
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue