mirror of
https://github.com/smogon/sprites.git
synced 2026-10-01 22:02:15 -05:00
Remove tup: delete Lua build config, tupctime, and tup state
The build is now driven by tools/build (see Buildfile.ts). tupctime is obsolete: the new tool hashes content behind a stat cache, so ctime/mtime churn without content changes no longer causes rebuilds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
/build/
|
||||
.tup
|
||||
node_modules
|
||||
pnpm-debug.log
|
||||
tup.config
|
||||
.cache/
|
||||
.build/
|
||||
build.config
|
||||
|
||||
55
README.md
55
README.md
@@ -6,14 +6,13 @@
|
||||
|
||||
This project depends on
|
||||
|
||||
- [tup](http://gittup.org/tup/)
|
||||
- [ImageMagick](http://www.imagemagick.org/) >= 7
|
||||
- [gifsicle](https://www.lcdf.org/gifsicle/)
|
||||
- [AdvPng](http://www.advancemame.it/doc-advpng.html) (optional)
|
||||
- [OptiPNG](http://optipng.sourceforge.net/) (optional)
|
||||
- [pngquant](https://pngquant.org/) (optional)
|
||||
- [pnpm](https://pnpm.js.org)
|
||||
- [node.js](https://nodejs.org) >= 24
|
||||
- [wine](https://www.winehq.org/) (optional)
|
||||
- cwebp
|
||||
|
||||
### Windows
|
||||
@@ -23,21 +22,10 @@ Windows binaries of these dependencies can be found on the download pages of the
|
||||
### Linux
|
||||
|
||||
```
|
||||
$ sudo apt install nodejs imagemagick advancecomp optipng pngquant wine webp
|
||||
$ sudo apt install nodejs imagemagick gifsicle advancecomp optipng pngquant webp
|
||||
$ sudo npm install -g pnpm
|
||||
```
|
||||
|
||||
Build tup from source:
|
||||
|
||||
```
|
||||
$ sudo apt install build-essential pkg-config fuse3 libfuse3-dev libpcre3-dev
|
||||
$ git clone git://github.com/gittup/tup.git
|
||||
$ cd tup
|
||||
$ ./bootstrap.sh
|
||||
$ sudo cp tup /usr/local/bin/tup
|
||||
$ sudo cp tup.1 /usr/local/share/man
|
||||
```
|
||||
|
||||
You may have to build imagemagick from source to get version 7.
|
||||
|
||||
```
|
||||
@@ -55,8 +43,7 @@ $ sudo ldconfig /usr/local/lib
|
||||
Using [`brew`](https://brew.sh/) on a macOS:
|
||||
|
||||
```
|
||||
$ brew cask install osxfuse wine-stable
|
||||
$ brew install tup imagemagick advancecomp optipng pngquant
|
||||
$ brew install imagemagick gifsicle advancecomp optipng pngquant webp
|
||||
```
|
||||
|
||||
## Building
|
||||
@@ -64,34 +51,44 @@ $ brew install tup imagemagick advancecomp optipng pngquant
|
||||
Install dependencies once with `pnpm install`. Then, to build:
|
||||
|
||||
```
|
||||
$ tup
|
||||
$ node tools/build/index.ts
|
||||
```
|
||||
|
||||
The rules live in `Buildfile.ts`. Build state (content hashes, rule records)
|
||||
is kept in `.build/`; outputs of removed rules are deleted automatically, and
|
||||
renamed sources are detected and their outputs copied instead of rebuilt.
|
||||
|
||||
Useful flags: `-j <n>` parallelism, `-n` dry run, `-v` verbose,
|
||||
`--adopt` record already-existing `build/` outputs as up to date instead of
|
||||
rebuilding them (useful when `build/` was produced elsewhere).
|
||||
|
||||
## Configuration
|
||||
|
||||
Build settings are configurable in `tup.config`.
|
||||
Build settings are configurable in `build.config` (not tracked by git).
|
||||
|
||||
- `CONFIG_DEFAULT_OPTIPNG`: Command line to pass to `optipng`.
|
||||
- `CONFIG_DEFAULT_ADVPNG`: Command line to pass to `advpng`.
|
||||
- `CONFIG_DEFAULT_PNGQUANT`: Command line to pass to `pngquant`.
|
||||
- `DEFAULT_OPTIPNG`: Command line to pass to `optipng`.
|
||||
- `DEFAULT_ADVPNG`: Command line to pass to `advpng`.
|
||||
- `DEFAULT_PNGQUANT`: Command line to pass to `pngquant`.
|
||||
|
||||
There are src-specific versions of these settings:
|
||||
|
||||
- `CONFIG_TRAINERS_<PROGRAM>`: Compression options for `trainers/` only.
|
||||
- `CONFIG_DEX_<PROGRAM>`: Compression options for `dex/` only.
|
||||
- `CONFIG_MODELS_<PROGRAM>`: Compression options for `models/` only.
|
||||
- `CONFIG_SPRITESHEET_<PROGRAM>`: Compression options for spritesheets only.
|
||||
- `CONFIG_MINISPRITE_<PROGRAM>`: Compression options for `minisprites/` only.
|
||||
- `TRAINERS_<PROGRAM>`: Compression options for `trainers/` only.
|
||||
- `DEX_<PROGRAM>`: Compression options for `dex/` only.
|
||||
- `MODELS_<PROGRAM>`: Compression options for `models/` only.
|
||||
- `SPRITESHEET_<PROGRAM>`: Compression options for spritesheets only.
|
||||
- `MINISPRITE_<PROGRAM>`: Compression options for `minisprites/` only.
|
||||
|
||||
For example, these settings reflect the compression settings for the files chaos uploaded in `src/`:
|
||||
```
|
||||
CONFIG_DEFAULT_OPTIPNG=-o7
|
||||
CONFIG_DEFAULT_ADVPNG=-z4 -i5000
|
||||
DEFAULT_OPTIPNG=-o7
|
||||
DEFAULT_ADVPNG=-z4 -i5000
|
||||
```
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Tup, like Git, tracks files, not directories. If you `readdir()` and forget to declare a dependency it won't catch it, like it would for `read()`. You can work around this by having build tools `stat()` any filenames they acquire.
|
||||
- The build tool only tracks the inputs a rule declares. If a build tool reads
|
||||
files that aren't on its command line (e.g. it does a `readdir()`), declare
|
||||
them with the rule's `deps:` in `Buildfile.ts` so changes are detected.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
257
Tupfile.lua
257
Tupfile.lua
@@ -1,257 +0,0 @@
|
||||
|
||||
-- Generate uniform size minisprites
|
||||
|
||||
foreach_rule(
|
||||
"src/minisprites/pokemon/gen6/*.png",
|
||||
{
|
||||
display="pad g6 minisprite %f",
|
||||
pad{w=40, h=30},
|
||||
compresspng{config="MINISPRITE"}
|
||||
},
|
||||
"build/gen6-minisprites-padded/%b"
|
||||
)
|
||||
|
||||
foreach_rule(
|
||||
"src/minisprites/items/*.png",
|
||||
{
|
||||
display="pad item minisprite %f",
|
||||
pad{w=24, h=24},
|
||||
compresspng{config="MINISPRITE"}
|
||||
},
|
||||
"build/item-minisprites-padded/%b"
|
||||
)
|
||||
|
||||
foreach_rule(
|
||||
"src/minisprites/pokemon/gen6/*.png",
|
||||
{
|
||||
display="trim g6 minisprite %f",
|
||||
trimimg{},
|
||||
compresspng{config="MINISPRITE"}
|
||||
},
|
||||
"build/gen6-minisprites-trimmed/%b"
|
||||
)
|
||||
|
||||
foreach_rule(
|
||||
"src/minisprites/items/*.png",
|
||||
{
|
||||
display="trim item minisprite %f",
|
||||
trimimg{},
|
||||
compresspng{config="MINISPRITE"}
|
||||
},
|
||||
"build/item-minisprites-trimmed/%b"
|
||||
)
|
||||
|
||||
-- Gen 9
|
||||
|
||||
foreach_rule(
|
||||
"src/gen9species/*.png",
|
||||
{
|
||||
display="96x96 %f",
|
||||
-- TODO, add customizable compression for gif
|
||||
-- ... or investigate using webp instead of both png/gif here
|
||||
"magick convert %f -trim +repage -resize 90x90 %o",
|
||||
"gifsicle -O3 -b %o"
|
||||
},
|
||||
"build/gen9-modelslike/%B.gif"
|
||||
)
|
||||
|
||||
-- Gen 10
|
||||
|
||||
foreach_rule(
|
||||
"src/champions/*.png",
|
||||
{
|
||||
display="96x96 %f",
|
||||
-- TODO, add customizable compression for gif
|
||||
-- ... or investigate using webp instead of both png/gif here
|
||||
"magick convert %f -trim +repage -resize 90x90 %o",
|
||||
"gifsicle -O3 -b %o"
|
||||
},
|
||||
"build/gen10-modelslike/%B.gif"
|
||||
)
|
||||
|
||||
-- Gen 5 CAPs...
|
||||
|
||||
foreach_rule(
|
||||
"src/sprites/gen5/*.png",
|
||||
{
|
||||
-- TODO, add customizable compression for gif
|
||||
-- ... or investigate using webp instead of both png/gif here
|
||||
"magick convert %f %o",
|
||||
"gifsicle -O3 -b %o"
|
||||
},
|
||||
"build/gen5-gif/%B.gif"
|
||||
)
|
||||
|
||||
-- PS spritesheet
|
||||
|
||||
rule(
|
||||
"ps-pokemon.sheet.mjs",
|
||||
{
|
||||
display="ps pokemon sheet",
|
||||
"node tools/sheet/index.ts %f %o",
|
||||
compresspng{config="SPRITESHEET"}
|
||||
},
|
||||
"build/ps/pokemonicons-sheet.png"
|
||||
)
|
||||
|
||||
-- TODO: reenable when trainers are moved
|
||||
-- rule{
|
||||
-- display="ps trainers sheet",
|
||||
-- {"ps-trainers.sheet.mjs"},
|
||||
-- {
|
||||
-- "node tools/sheet/index.ts %f %o",
|
||||
-- compresspng{config="SPRITESHEET"}
|
||||
-- },
|
||||
-- {"build/ps/trainers-sheet.png"}
|
||||
-- }
|
||||
|
||||
rule(
|
||||
"ps-items.sheet.mjs",
|
||||
{
|
||||
display="ps items sheet",
|
||||
"node tools/sheet/index.ts %f %o",
|
||||
compresspng{config="SPRITESHEET"}
|
||||
},
|
||||
"build/ps/itemicons-sheet.png"
|
||||
)
|
||||
|
||||
-- PS pokeball icons
|
||||
|
||||
local balls = {
|
||||
"src/_uncategorized/noncanonical/ui/battle/Ball-Normal.png",
|
||||
"src/_uncategorized/noncanonical/ui/battle/Ball-Sick.png",
|
||||
"src/_uncategorized/noncanonical/ui/battle/Ball-Null.png",
|
||||
}
|
||||
|
||||
rule(
|
||||
balls,
|
||||
{
|
||||
display="pokemonicons-pokeball-sheet",
|
||||
"magick convert -background transparent -gravity center -extent 40x30 %f +append %o",
|
||||
compresspng{config="SPRITESHEET"}
|
||||
},
|
||||
"build/ps/pokemonicons-pokeball-sheet.png"
|
||||
)
|
||||
|
||||
-- Smogdex minisprites (webp)
|
||||
|
||||
foreach_rule(
|
||||
spriteglob({"src/minisprites/pokemon/gen6/*", "src/minisprites/items/*"}, {a = false}),
|
||||
{
|
||||
display="webp minisprite %f",
|
||||
"cwebp -z 9 %f -o %o"
|
||||
},
|
||||
"build/smogon/minisprites/%B.webp"
|
||||
)
|
||||
|
||||
-- Smogdex spritesheet
|
||||
|
||||
|
||||
rule(
|
||||
spriteglob({"src/minisprites/pokemon/gen6/*", "src/minisprites/items/*"}, {a = false}),
|
||||
{
|
||||
display="smogdex sheet",
|
||||
"node tools/smogdexspritesheet/index.ts --image build/smogon/spritesheet.png --stylesheet build/smogon/spritesheet.css -- %f",
|
||||
"cwebp -z 9 build/smogon/spritesheet.png -o build/smogon/spritesheet.webp",
|
||||
"rm build/smogon/spritesheet.png"
|
||||
},
|
||||
{"build/smogon/spritesheet.webp", "build/smogon/spritesheet.css"}
|
||||
)
|
||||
|
||||
-- Smogdex social images
|
||||
|
||||
local input = spriteglob({"src/models/*"}, {b = false, s = false})
|
||||
|
||||
-- Copied from the CAP bit, dedup, should probably make a routine for it
|
||||
local dexSet = {}
|
||||
for file in iter(input) do
|
||||
dexSet[tup.base(file)] = true
|
||||
end
|
||||
|
||||
for file in iter(spriteglob({"src/gen9species/*"}, {b = false, s = false})) do
|
||||
local base = tup.base(file)
|
||||
if dexSet[base] then
|
||||
goto continue
|
||||
end
|
||||
input += file
|
||||
dexSet[base] = true
|
||||
::continue::
|
||||
end
|
||||
|
||||
foreach_rule(
|
||||
input,
|
||||
{
|
||||
display="fbsprite %f",
|
||||
"magick convert \"%f[0]\" -trim -resize 150x150 -background white -gravity center -extent 198x198 -bordercolor black -border 1 %o",
|
||||
compresspng{config="MODELS"}
|
||||
},
|
||||
"build/smogon/fbsprites/xy/%B.png"
|
||||
)
|
||||
|
||||
foreach_rule(
|
||||
input,
|
||||
{
|
||||
display="twittersprite %f",
|
||||
"magick convert \"%f[0]\" -trim -resize 115x115 -background white -gravity center -extent 120x120 %o",
|
||||
compresspng{config="MODELS"}
|
||||
},
|
||||
"build/smogon/twittersprites/xy/%B.png"
|
||||
)
|
||||
|
||||
|
||||
-- Trainers
|
||||
|
||||
-- TODO: reenable when trainers are moved
|
||||
-- foreach_rule{
|
||||
-- display="pad trainer %f",
|
||||
-- {"src/canonical/trainers/*"},
|
||||
-- {
|
||||
-- pad{w=80, h=80},
|
||||
-- compresspng{config="TRAINERS"}
|
||||
-- },
|
||||
-- {"build/padded-trainers/canonical/%b"}
|
||||
-- }
|
||||
|
||||
-- Padded Dex
|
||||
|
||||
local dexOutput = foreach_rule(
|
||||
"src/dex/*",
|
||||
{
|
||||
display="pad dex %f",
|
||||
pad{w=120, h=120},
|
||||
compresspng{config="DEX"}
|
||||
},
|
||||
"build/padded-dex/%b"
|
||||
)
|
||||
|
||||
|
||||
-- Build missing CAP dex
|
||||
|
||||
local dexSet = {}
|
||||
for file in iter(dexOutput) do
|
||||
dexSet[tup.base(file)] = true
|
||||
end
|
||||
|
||||
local dexMissing = {}
|
||||
for file in iter(spriteglob(
|
||||
{"src/sprites/gen5/*.gif", "src/models/*.gif"},
|
||||
{b = false, s = false})) do
|
||||
local base = tup.base(file)
|
||||
if dexSet[base] then
|
||||
goto continue
|
||||
end
|
||||
dexMissing += file
|
||||
dexSet[base] = true
|
||||
::continue::
|
||||
end
|
||||
|
||||
foreach_rule(
|
||||
dexMissing,
|
||||
{
|
||||
display="missing dex %B",
|
||||
"magick convert \"%f[0]\" -trim %o",
|
||||
"magick mogrify -background transparent -gravity center -resize \"120x120>\" -extent 120x120 %o",
|
||||
compresspng{config="DEX"}
|
||||
},
|
||||
"build/padded-dex/%B.png"
|
||||
)
|
||||
53
Tuprules.lua
53
Tuprules.lua
@@ -1,53 +0,0 @@
|
||||
|
||||
--tup.include("util/strict.lua")
|
||||
tup.include("util/lua-ext.lua")
|
||||
tup.include("util/tup-ext.lua")
|
||||
tup.include("util/sprites.lua")
|
||||
|
||||
ROOTDIR = tup.getcwd()
|
||||
|
||||
function pad(opts)
|
||||
return rep{
|
||||
"magick convert ${input} -background transparent -gravity center -extent ${w}x${h} ${output}",
|
||||
input = opts.input or "%f",
|
||||
output = opts.output or "%o",
|
||||
w = opts.w,
|
||||
h = opts.h
|
||||
}
|
||||
end
|
||||
|
||||
function trimimg(opts) -- Can't just be trim because of the string function...
|
||||
return rep{
|
||||
"magick convert ${input} -trim ${output}",
|
||||
input = opts.input or "%f",
|
||||
output = opts.output or "%o",
|
||||
}
|
||||
end
|
||||
|
||||
local function compressopts(program, copts)
|
||||
copts.pngquant = getconfig(program .. "_PNGQUANT") or copts.pngquant
|
||||
copts.optipng = getconfig(program .. "_OPTIPNG") or copts.optipng
|
||||
copts.advpng = getconfig(program .. "_ADVPNG") or copts.advpng
|
||||
end
|
||||
|
||||
function compresspng(opts)
|
||||
local cmds = {}
|
||||
local output = opts.output or "%o"
|
||||
local copts = {}
|
||||
compressopts("DEFAULT", copts)
|
||||
if opts.config then
|
||||
compressopts(opts.config, copts)
|
||||
end
|
||||
if copts.pngquant then
|
||||
-- -f -o necessary to overwrite existing file
|
||||
cmds += rep{"pngquant -f -o ${output} ${opts} ${output}", opts=copts.pngquant, output=output}
|
||||
end
|
||||
if copts.optipng then
|
||||
cmds += rep{"optipng -q ${opts} ${output}", opts=copts.optipng, output=output}
|
||||
end
|
||||
if copts.advpng then
|
||||
cmds += rep{"advpng -q ${opts} ${output}", opts=copts.advpng, output=output}
|
||||
end
|
||||
|
||||
return cmds
|
||||
end
|
||||
18
pnpm-lock.yaml
generated
18
pnpm-lock.yaml
generated
@@ -118,24 +118,6 @@ importers:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
|
||||
tools/tupctime:
|
||||
dependencies:
|
||||
'@types/better-sqlite3':
|
||||
specifier: ^7.6.13
|
||||
version: 7.6.13
|
||||
'@types/debug':
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.13
|
||||
'@types/node':
|
||||
specifier: ^24.10.7
|
||||
version: 24.13.3
|
||||
better-sqlite3:
|
||||
specifier: ^12.4.1
|
||||
version: 12.11.1
|
||||
debug:
|
||||
specifier: ^4.1.1
|
||||
version: 4.4.3
|
||||
|
||||
packages:
|
||||
|
||||
'@types/better-sqlite3@7.6.13':
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import pathlib from 'path';
|
||||
import Database from 'better-sqlite3';
|
||||
import debugfn from 'debug';
|
||||
|
||||
const debug = debugfn('tupctime');
|
||||
|
||||
const start = process.argv[2];
|
||||
if (start !== undefined) {
|
||||
process.chdir(start);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (fs.existsSync('.tup')) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (process.cwd() === '/') {
|
||||
throw new Error("Can't find tup root.");
|
||||
}
|
||||
|
||||
process.chdir('..');
|
||||
}
|
||||
|
||||
const db = new Database('.tup/db');
|
||||
|
||||
const TUP_DB_FILE = 0;
|
||||
const TUP_DB_DIR = 2;
|
||||
const TUP_DB_GENERATED = 4;
|
||||
const TUP_DB_GENERATED_DIR = 7;
|
||||
|
||||
// ^ and $ are virtual directories for environment variables
|
||||
const traversalQuery = db.prepare<[number], {id: number, type: number, ctime: number, name: string}>(`
|
||||
SELECT id, type, mtime as ctime, name
|
||||
FROM node
|
||||
WHERE dir = ?
|
||||
AND type IN (${TUP_DB_FILE}, ${TUP_DB_DIR}, ${TUP_DB_GENERATED}, ${TUP_DB_GENERATED_DIR})
|
||||
AND name NOT IN ('^', '$')`);
|
||||
const updateQuery = db.prepare(`UPDATE node SET mtime = ? WHERE id = ?`);
|
||||
|
||||
function update() {
|
||||
const stack : {isDir: boolean, id : number, path: string, ctime?: bigint, name?: string}[] = [{isDir: true, id: 0, path: "."}];
|
||||
|
||||
let item;
|
||||
while (item = stack.pop()) {
|
||||
if (!fs.existsSync(item.path)) {
|
||||
debug(`Ignoring ${item.path}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.isDir) {
|
||||
for (const {id, type, ctime, name} of traversalQuery.iterate(item.id)) {
|
||||
const isDir = [TUP_DB_DIR, TUP_DB_GENERATED_DIR].includes(type);
|
||||
const path = pathlib.join(item.path, name);
|
||||
stack.push({isDir, id, ctime: BigInt(ctime), path});
|
||||
}
|
||||
} else {
|
||||
// bigint calculation is necessary to avoid rounding errors/spurious update
|
||||
//
|
||||
// typescript doesn't understand second argument to lstatSync, or
|
||||
// that division can work between a number and bigint, so ignore
|
||||
//
|
||||
// @ts-ignore
|
||||
const ctime = fs.lstatSync(item.path, {bigint: true}).ctimeMs / 1000n;
|
||||
if (ctime !== item.ctime) {
|
||||
debug(`Updating ${item.path}: ${ctime}, ${item.ctime}`);
|
||||
if (updateQuery.run(ctime, item.id).changes !== 1)
|
||||
throw new Error(`Couldn't update ${item.path}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.transaction(update)();
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/node": "^24.10.7",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"debug": "^4.1.1"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig-base"
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
"references": [
|
||||
{"path": "./tsconfig.json"},
|
||||
{"path": "tools/build"},
|
||||
{"path": "tools/tupctime"},
|
||||
{"path": "tools/deploy"},
|
||||
{"path": "tools/sheet"},
|
||||
{"path": "tools/smogdexspritesheet"},
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
|
||||
--
|
||||
-- Strings
|
||||
--
|
||||
|
||||
function trim(s)
|
||||
return s:gsub("^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
function rep(args)
|
||||
local str = args[1]
|
||||
local vars = args
|
||||
local function fn(var)
|
||||
local v = vars[var]
|
||||
if v == nil then
|
||||
error("unknown substitution: " .. var)
|
||||
end
|
||||
return v
|
||||
end
|
||||
-- Note: not equivalent to return str:gsub(...) due to multiple return values!
|
||||
str = str:gsub("${(%a+)}", fn)
|
||||
return str
|
||||
end
|
||||
|
||||
--
|
||||
-- Tables
|
||||
--
|
||||
|
||||
-- Allow `for v in iter(table)` instead of `for _, v in ipairs(table)`
|
||||
function iter(table)
|
||||
local i = 1
|
||||
return function ()
|
||||
local v = table[i]
|
||||
i = i + 1
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
function astable(table)
|
||||
local t = type(table)
|
||||
if t == 'table' then
|
||||
return table
|
||||
elseif t == 'string' then
|
||||
return {table}
|
||||
elseif t == 'nil' then
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Adapted from premake
|
||||
function flatten(arr)
|
||||
local result = {}
|
||||
|
||||
local function flatten(arr)
|
||||
for v in iter(arr) do
|
||||
if type(v) == "table" then
|
||||
flatten(v)
|
||||
else
|
||||
table.insert(result, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
flatten(arr)
|
||||
return result
|
||||
end
|
||||
|
||||
-- Adapted from https://stackoverflow.com/a/53038524
|
||||
function filter(t, fn)
|
||||
local j, n = 1, #t
|
||||
|
||||
for i=1,n do
|
||||
if fn(t[i]) then
|
||||
-- Move i's kept value to j's position, if it's not already there.
|
||||
if (i ~= j) then
|
||||
t[j] = t[i]
|
||||
t[i] = nil
|
||||
end
|
||||
j = j + 1 -- Increment position of where we'll place the next kept value.
|
||||
else
|
||||
t[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
-- Similar to `parseFilename` in data/lib
|
||||
function spritedata(basename)
|
||||
local iter = basename:gmatch("[^-]+")
|
||||
local result = {id = iter(), data = {}}
|
||||
for flagtext in iter do
|
||||
if flagtext:len() == 1 then
|
||||
result.data[flagtext] = true
|
||||
else
|
||||
local flag = flagtext:sub(1, 1)
|
||||
local text = flagtext:sub(2)
|
||||
result.data[flag] = text
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function spriteglob(pat, flagspec)
|
||||
local results = glob(pat)
|
||||
local function fn(filename)
|
||||
local sd = spritedata(tup.base(filename))
|
||||
for k, v in pairs(flagspec or {}) do
|
||||
-- Make sure both are booleans
|
||||
if not not v ~= not not sd.data[k] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
filter(results, fn)
|
||||
return results
|
||||
end
|
||||
@@ -1,32 +0,0 @@
|
||||
--
|
||||
-- strict.lua
|
||||
-- checks uses of undeclared global variables
|
||||
-- All global variables must be 'declared' through a regular assignment
|
||||
-- (even assigning nil will do) in a main chunk before being used
|
||||
-- anywhere or assigned to inside a function.
|
||||
--
|
||||
|
||||
-- NOTE: this file has been edited to remove mt.__newindex
|
||||
|
||||
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
|
||||
|
||||
local mt = getmetatable(_G)
|
||||
if mt == nil then
|
||||
mt = {}
|
||||
setmetatable(_G, mt)
|
||||
end
|
||||
|
||||
mt.__declared = {}
|
||||
|
||||
local function what ()
|
||||
local d = getinfo(3, "S")
|
||||
return d and d.what or "C"
|
||||
end
|
||||
|
||||
mt.__index = function (t, n)
|
||||
if not mt.__declared[n] and what() ~= "C" then
|
||||
error("variable '"..n.."' is not declared", 2)
|
||||
end
|
||||
return rawget(t, n)
|
||||
end
|
||||
|
||||
104
util/tup-ext.lua
104
util/tup-ext.lua
@@ -1,104 +0,0 @@
|
||||
|
||||
--
|
||||
-- Configuration
|
||||
--
|
||||
|
||||
-- getconfig returning an empty string on absence is inconvenient
|
||||
function getconfig(str)
|
||||
local v = tup.getconfig(str)
|
||||
if v == "" then
|
||||
return nil
|
||||
else
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
function booleanconfig(str)
|
||||
local v = getconfig(str)
|
||||
if v == nil or v == "false" then
|
||||
return false
|
||||
elseif v == "true" then
|
||||
return true
|
||||
else
|
||||
error("boolean config must be true, false, or empty")
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Globs
|
||||
--
|
||||
|
||||
-- Workaround weird bug pre reported, where final path segment has two //
|
||||
-- EDIT: AND workaround a bug Marty reported on Windows, where final path segment has a /\
|
||||
local old_tup_glob = tup.glob
|
||||
function tup.glob(pat)
|
||||
local results = old_tup_glob(pat)
|
||||
for i = 1, #results do
|
||||
results[i] = results[i]:gsub("/\\", "/"):gsub("//", "/")
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
-- A glob pattern is either an interpolated string, or a table of glob patterns
|
||||
-- globpat_normalize("foo/*") --> {"foo/*"}
|
||||
-- globpat_normalize({"foo/*"}) --> {"foo/*"}
|
||||
local function globpat_normalize(pat)
|
||||
if type(pat) == "string" then
|
||||
return {pat}
|
||||
elseif type(pat) == 'table' then
|
||||
local result = {}
|
||||
for x in iter(pat) do
|
||||
result += globpat_normalize(x)
|
||||
end
|
||||
return result
|
||||
else
|
||||
error("bad globpat")
|
||||
end
|
||||
end
|
||||
|
||||
function glob(pat, opts)
|
||||
local results = {}
|
||||
for pat in iter(globpat_normalize(pat)) do
|
||||
for file in iter(tup.glob(pat)) do
|
||||
table.insert(results, file)
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
|
||||
-- Commands
|
||||
|
||||
local function cspec2cmd(cmdSpec)
|
||||
local cmd = ""
|
||||
for newcmd in iter(flatten(astable(cmdSpec))) do
|
||||
newcmd = trim(newcmd)
|
||||
if cmd ~= "" then
|
||||
cmd = cmd .. " &&\n"
|
||||
end
|
||||
cmd = cmd .. newcmd
|
||||
end
|
||||
-- Abstract over tup's arcane display override
|
||||
if cmdSpec.display then
|
||||
cmd = rep{"^ ${display}^ ${cmd}", display=cmdSpec.display, cmd=cmd}
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
local function do_rule(input, cspec, output, foreach)
|
||||
local cmd = cspec2cmd(cspec)
|
||||
if foreach then
|
||||
return tup.foreach_rule(input, cmd, output)
|
||||
else
|
||||
return tup.rule(input, cmd, output)
|
||||
end
|
||||
end
|
||||
|
||||
function rule(input, cmd, output)
|
||||
return do_rule(input, cmd, output, false)
|
||||
end
|
||||
|
||||
function foreach_rule(input, cmd, output)
|
||||
return do_rule(input, cmd, output, true)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user