aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
l---------.direnv/flake-profile1
-rw-r--r--.direnv/flake-profile.rc1515
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock61
-rw-r--r--Cargo.toml10
-rw-r--r--flake.lock91
-rw-r--r--flake.nix54
-rw-r--r--src/app.rs447
-rw-r--r--src/consts.rs6
-rw-r--r--src/main.rs10
-rw-r--r--src/undo.rs133
11 files changed, 2329 insertions, 0 deletions
diff --git a/.direnv/flake-profile b/.direnv/flake-profile
new file mode 120000
index 0000000..b4c9021
--- /dev/null
+++ b/.direnv/flake-profile
@@ -0,0 +1 @@
/nix/store/9jfvhch5cimq3cp7jkch719r68jjhpld-nix-shell-env \ No newline at end of file
diff --git a/.direnv/flake-profile.rc b/.direnv/flake-profile.rc
new file mode 100644
index 0000000..98ce9c2
--- /dev/null
+++ b/.direnv/flake-profile.rc
@@ -0,0 +1,1515 @@
1unset shellHook
2nix_saved_PATH="$PATH"
3AR=ar
4export AR
5AS=as
6export AS
7BASH=/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin/bash
8CC=gcc
9export CC
10CONFIG_SHELL=/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin/bash
11export CONFIG_SHELL
12CXX=g++
13export CXX
14DIRSTACK=()
15GROUPS=()
16HOSTTYPE=x86_64
17HOST_PATH=/nix/store/w21pgi6691202iafl0cnr7hk6wvvdz3n-coreutils-8.32/bin:/nix/store/70qfzyc5hmvssj7d0dqkqhn827xwgmy3-findutils-4.7.0/bin:/nix/store/49gllxpk2f3dcyx4q8cgj0w39p10s209-diffutils-3.7/bin:/nix/store/n0516fv7vwjbl41nl4q58w9si80ab93i-gnused-4.8/bin:/nix/store/3mx947xcmsyk79izqc8ifv31qafp80pc-gnugrep-3.6/bin:/nix/store/xj242al7hq36baipvj2qdfwf7dljq31f-gawk-5.1.0/bin:/nix/store/39qygzkhcb9i684w3fhr2kkz5gmcyycd-gnutar-1.32/bin:/nix/store/bnpadh78k2qf3hshwapl5s51dj87yykp-gzip-1.10/bin:/nix/store/k514dbajcp0wb32rnglvc940gldj549h-bzip2-1.0.6.0.1-bin/bin:/nix/store/a2q8136fwwkvigxq3icyd70zllvw45lc-gnumake-4.3/bin:/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin:/nix/store/kh9l9zpjxsdnx6zi91pk3yich8ivwii4-patch-2.7.6/bin:/nix/store/bap20qjfwfzl49zz11prqdyfdmq7w8ca-xz-5.2.5-bin/bin
18export HOST_PATH
19IFS=$' \t\n'
20IN_NIX_SHELL=impure
21export IN_NIX_SHELL
22LD=ld
23export LD
24MACHTYPE=x86_64-unknown-linux-gnu
25NIX_BINTOOLS=/nix/store/jlyaypckqlapwqpck7jscs6qcdwg9bk3-binutils-wrapper-2.35.1
26export NIX_BINTOOLS
27NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu=1
28export NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
29NIX_BUILD_CORES=8
30export NIX_BUILD_CORES
31NIX_CC=/nix/store/ca37d3qrydh0wpw40kswsx30j8dyzxh2-gcc-wrapper-10.2.0
32export NIX_CC
33NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu=1
34export NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu
35NIX_CFLAGS_COMPILE=' -frandom-seed=9jfvhch5ci -isystem /nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/include -isystem /nix/store/5sphs39wg8gyy5pcrcml5pyj1wrn9jil-libGL-1.3.2-dev/include -isystem /nix/store/skwk7hmzwpjvhgjf1y633l01p114xc6n-libglvnd-1.3.2-dev/include -isystem /nix/store/3y6j0k82fdp3ifnq655yv1ad1ih7r70j-libX11-1.7.0-dev/include -isystem /nix/store/vjimy2jlifszmmms664373jdw0yiz4ln-xorgproto-2020.1/include -isystem /nix/store/6xg7mhwh2k7i27r44lx679diwd7ahq6d-libxcb-1.14-dev/include -isystem /nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/include -isystem /nix/store/5sphs39wg8gyy5pcrcml5pyj1wrn9jil-libGL-1.3.2-dev/include -isystem /nix/store/skwk7hmzwpjvhgjf1y633l01p114xc6n-libglvnd-1.3.2-dev/include -isystem /nix/store/3y6j0k82fdp3ifnq655yv1ad1ih7r70j-libX11-1.7.0-dev/include -isystem /nix/store/vjimy2jlifszmmms664373jdw0yiz4ln-xorgproto-2020.1/include -isystem /nix/store/6xg7mhwh2k7i27r44lx679diwd7ahq6d-libxcb-1.14-dev/include'
36export NIX_CFLAGS_COMPILE
37NIX_ENFORCE_NO_NATIVE=1
38export NIX_ENFORCE_NO_NATIVE
39NIX_HARDENING_ENABLE='fortify stackprotector pic strictoverflow format relro bindnow'
40export NIX_HARDENING_ENABLE
41NIX_INDENT_MAKE=1
42export NIX_INDENT_MAKE
43NIX_LDFLAGS='-rpath /home/np/code/rust/sdl-tests/outputs/out/lib64 -rpath /home/np/code/rust/sdl-tests/outputs/out/lib -L/nix/store/21qrj65fsf1glbnw07f6qnkavgi9rmzw-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/lib -L/nix/store/q47zvd16rpppdz4ami7wrx8318fwjpfk-libGL-1.3.2/lib -L/nix/store/sv2xsv21136linyi477ijd00gmihlwbp-libglvnd-1.3.2/lib -L/nix/store/jmpwz53zajcld1xb8zslh8g8p19d8wzl-libxcb-1.14/lib -L/nix/store/80p7f1sw4szy7w1mhqxd268n6i1gmybs-libX11-1.7.0/lib -L/nix/store/k58g6biziz8vpk6zw5n9bym2c6wxjrpd-SDL2-2.0.14/lib -L/nix/store/21qrj65fsf1glbnw07f6qnkavgi9rmzw-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/lib -L/nix/store/q47zvd16rpppdz4ami7wrx8318fwjpfk-libGL-1.3.2/lib -L/nix/store/sv2xsv21136linyi477ijd00gmihlwbp-libglvnd-1.3.2/lib -L/nix/store/jmpwz53zajcld1xb8zslh8g8p19d8wzl-libxcb-1.14/lib -L/nix/store/80p7f1sw4szy7w1mhqxd268n6i1gmybs-libX11-1.7.0/lib -L/nix/store/k58g6biziz8vpk6zw5n9bym2c6wxjrpd-SDL2-2.0.14/lib'
44export NIX_LDFLAGS
45NIX_LIB64_IN_SELF_RPATH=1
46NIX_NO_SELF_RPATH=1
47NIX_STORE=/nix/store
48export NIX_STORE
49NM=nm
50export NM
51OBJCOPY=objcopy
52export OBJCOPY
53OBJDUMP=objdump
54export OBJDUMP
55OPTERR=1
56OPTIND=1
57OSTYPE=linux-gnu
58PATH=/nix/store/21qrj65fsf1glbnw07f6qnkavgi9rmzw-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/bin:/nix/store/hpkjc41idlbwk82cnx0sw8mmkjjb5lvi-rust-analyzer-unstable-2021-03-01/bin:/nix/store/2h05zs2wbxv3zisz342k6acfm6awcnwl-cargo-1.49.0/bin:/nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/bin:/nix/store/zrh1cskgv6kc8bh7hzfii14h2nck33qv-patchelf-0.12/bin:/nix/store/ca37d3qrydh0wpw40kswsx30j8dyzxh2-gcc-wrapper-10.2.0/bin:/nix/store/sr0ci8f8pgby77fj4mpcl9bcgxji3676-gcc-10.2.0/bin:/nix/store/mbxi7rhg9pj2mfnzr7dk9cd46xdhnrf9-glibc-2.32-37-bin/bin:/nix/store/w21pgi6691202iafl0cnr7hk6wvvdz3n-coreutils-8.32/bin:/nix/store/jlyaypckqlapwqpck7jscs6qcdwg9bk3-binutils-wrapper-2.35.1/bin:/nix/store/cp1sa3xxvl71cypiinw2c62i5s33chlr-binutils-2.35.1/bin:/nix/store/mbxi7rhg9pj2mfnzr7dk9cd46xdhnrf9-glibc-2.32-37-bin/bin:/nix/store/w21pgi6691202iafl0cnr7hk6wvvdz3n-coreutils-8.32/bin:/nix/store/w21pgi6691202iafl0cnr7hk6wvvdz3n-coreutils-8.32/bin:/nix/store/70qfzyc5hmvssj7d0dqkqhn827xwgmy3-findutils-4.7.0/bin:/nix/store/49gllxpk2f3dcyx4q8cgj0w39p10s209-diffutils-3.7/bin:/nix/store/n0516fv7vwjbl41nl4q58w9si80ab93i-gnused-4.8/bin:/nix/store/3mx947xcmsyk79izqc8ifv31qafp80pc-gnugrep-3.6/bin:/nix/store/xj242al7hq36baipvj2qdfwf7dljq31f-gawk-5.1.0/bin:/nix/store/39qygzkhcb9i684w3fhr2kkz5gmcyycd-gnutar-1.32/bin:/nix/store/bnpadh78k2qf3hshwapl5s51dj87yykp-gzip-1.10/bin:/nix/store/k514dbajcp0wb32rnglvc940gldj549h-bzip2-1.0.6.0.1-bin/bin:/nix/store/a2q8136fwwkvigxq3icyd70zllvw45lc-gnumake-4.3/bin:/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin:/nix/store/kh9l9zpjxsdnx6zi91pk3yich8ivwii4-patch-2.7.6/bin:/nix/store/bap20qjfwfzl49zz11prqdyfdmq7w8ca-xz-5.2.5-bin/bin
59export PATH
60PIPESTATUS=([0]="0")
61PS4='+ '
62RANLIB=ranlib
63export RANLIB
64READELF=readelf
65export READELF
66RUST_SRC_PATH=/nix/store/wfv522w143y3yxx8ap3fkpjk9cnh1w3a-rust-lib-src
67export RUST_SRC_PATH
68SDL2_PATH='/nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/include/SDL2 /nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/include/SDL2 /nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/include/SDL2'
69export SDL2_PATH
70SHELL=/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin/bash
71export SHELL
72SIZE=size
73export SIZE
74SOURCE_DATE_EPOCH=315532800
75export SOURCE_DATE_EPOCH
76STRINGS=strings
77export STRINGS
78STRIP=strip
79export STRIP
80XDG_DATA_DIRS=/nix/store/21qrj65fsf1glbnw07f6qnkavgi9rmzw-rust-1.50.0-nightly-2020-12-22-bb1fbbf84/share:/nix/store/2h05zs2wbxv3zisz342k6acfm6awcnwl-cargo-1.49.0/share:/nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev/share:/nix/store/vjimy2jlifszmmms664373jdw0yiz4ln-xorgproto-2020.1/share:/nix/store/80p7f1sw4szy7w1mhqxd268n6i1gmybs-libX11-1.7.0/share:/nix/store/zrh1cskgv6kc8bh7hzfii14h2nck33qv-patchelf-0.12/share
81export XDG_DATA_DIRS
82_=export
83buildInputs=
84export buildInputs
85builder=/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin/bash
86export builder
87commonStripFlags=--enable-deterministic-archives
88configureFlags=
89export configureFlags
90defaultBuildInputs=
91defaultNativeBuildInputs='/nix/store/zrh1cskgv6kc8bh7hzfii14h2nck33qv-patchelf-0.12 /nix/store/mjjy30kxz775bhhi6j9phw81qh6dsbrf-move-docs.sh /nix/store/kxw6q8v6isaqjm702d71n2421cxamq68-make-symlinks-relative.sh /nix/store/cl3qd985p1yxyfkj96v0hqxiy3w69xq5-compress-man-pages.sh /nix/store/h54dzwd7rdh2jlcv91424csl6d0ccgjy-strip.sh /nix/store/bnj8d7mvbkg3vdb07yz74yhl3g107qq5-patch-shebangs.sh /nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh /nix/store/8zxndz5ag0p6s526c2xyllhk1nrn4c3i-audit-tmpdir.sh /nix/store/c8n9kcdddp9np665xz6ri61b383nxvz8-move-systemd-user-units.sh /nix/store/1i5y55x4b4m9qkx5dqbmr1r6bvrqbanw-multiple-outputs.sh /nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh /nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh /nix/store/ngg1cv31c8c7bcm2n8ww4g06nq7s4zhm-set-source-date-epoch-to-latest.sh /nix/store/pr6nzbscdpg94yvr151lrif2kg0csx7b-reproducible-builds.sh /nix/store/ca37d3qrydh0wpw40kswsx30j8dyzxh2-gcc-wrapper-10.2.0'
92depsBuildBuild=
93export depsBuildBuild
94depsBuildBuildPropagated=
95export depsBuildBuildPropagated
96depsBuildTarget=
97export depsBuildTarget
98depsBuildTargetPropagated=
99export depsBuildTargetPropagated
100depsHostHost=
101export depsHostHost
102depsHostHostPropagated=
103export depsHostHostPropagated
104depsTargetTarget=
105export depsTargetTarget
106depsTargetTargetPropagated=
107export depsTargetTargetPropagated
108doCheck=
109export doCheck
110doInstallCheck=
111export doInstallCheck
112dontAddDisableDepTrack=1
113export dontAddDisableDepTrack
114envBuildBuildHooks=([0]="addSDL2Path")
115envBuildHostHooks=([0]="addSDL2Path")
116envBuildTargetHooks=([0]="addSDL2Path")
117envHostHostHooks=([0]="ccWrapper_addCVars" [1]="bintoolsWrapper_addLDVars")
118envHostTargetHooks=([0]="ccWrapper_addCVars" [1]="bintoolsWrapper_addLDVars")
119fixupOutputHooks=([0]="if [ -z \"\${dontPatchELF-}\" ]; then patchELF \"\$prefix\"; fi" [1]="_makeSymlinksRelative" [2]="if [ -z \"\${dontGzipMan-}\" ]; then compressManPages \"\$prefix\"; fi" [3]="_doStrip" [4]="patchShebangsAuto" [5]="_pruneLibtoolFiles" [6]="if [[ -z \"\${noAuditTmpdir-}\" && -e \"\$prefix\" ]]; then auditTmpdir \"\$prefix\"; fi" [7]="_moveSystemdUserUnits" [8]="_moveSbin" [9]="_moveLib64")
120initialPath='/nix/store/w21pgi6691202iafl0cnr7hk6wvvdz3n-coreutils-8.32 /nix/store/70qfzyc5hmvssj7d0dqkqhn827xwgmy3-findutils-4.7.0 /nix/store/49gllxpk2f3dcyx4q8cgj0w39p10s209-diffutils-3.7 /nix/store/n0516fv7vwjbl41nl4q58w9si80ab93i-gnused-4.8 /nix/store/3mx947xcmsyk79izqc8ifv31qafp80pc-gnugrep-3.6 /nix/store/xj242al7hq36baipvj2qdfwf7dljq31f-gawk-5.1.0 /nix/store/39qygzkhcb9i684w3fhr2kkz5gmcyycd-gnutar-1.32 /nix/store/bnpadh78k2qf3hshwapl5s51dj87yykp-gzip-1.10 /nix/store/k514dbajcp0wb32rnglvc940gldj549h-bzip2-1.0.6.0.1-bin /nix/store/a2q8136fwwkvigxq3icyd70zllvw45lc-gnumake-4.3 /nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23 /nix/store/kh9l9zpjxsdnx6zi91pk3yich8ivwii4-patch-2.7.6 /nix/store/bap20qjfwfzl49zz11prqdyfdmq7w8ca-xz-5.2.5-bin'
121name=nix-shell
122export name
123nativeBuildInputs='/nix/store/21qrj65fsf1glbnw07f6qnkavgi9rmzw-rust-1.50.0-nightly-2020-12-22-bb1fbbf84 /nix/store/nnw3pf7h3grbi82rvaw16b57w2px7wq5-rust-src-1.50.0-nightly-2020-12-22-bb1fbbf84 /nix/store/hpkjc41idlbwk82cnx0sw8mmkjjb5lvi-rust-analyzer-unstable-2021-03-01 /nix/store/2h05zs2wbxv3zisz342k6acfm6awcnwl-cargo-1.49.0 /nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev'
124export nativeBuildInputs
125nobuildPhase=$'echo\necho "This derivation is not meant to be built, aborting";\necho\nexit 1\n'
126export nobuildPhase
127out=/home/np/code/rust/sdl-tests/outputs/out
128export out
129outputBin=out
130outputDev=out
131outputDevdoc=REMOVE
132outputDevman=out
133outputDoc=out
134outputInclude=out
135outputInfo=out
136outputLib=out
137outputMan=out
138outputs=out
139export outputs
140patches=
141export patches
142phases=nobuildPhase
143export phases
144pkg=/nix/store/ca37d3qrydh0wpw40kswsx30j8dyzxh2-gcc-wrapper-10.2.0
145pkgsBuildHost=([0]="/nix/store/21qrj65fsf1glbnw07f6qnkavgi9rmzw-rust-1.50.0-nightly-2020-12-22-bb1fbbf84" [1]="/nix/store/nnw3pf7h3grbi82rvaw16b57w2px7wq5-rust-src-1.50.0-nightly-2020-12-22-bb1fbbf84" [2]="/nix/store/hpkjc41idlbwk82cnx0sw8mmkjjb5lvi-rust-analyzer-unstable-2021-03-01" [3]="/nix/store/2h05zs2wbxv3zisz342k6acfm6awcnwl-cargo-1.49.0" [4]="/nix/store/gkqv9ia81dmmjqcn46ih5yirbzjy7s9p-SDL2-2.0.14-dev" [5]="/nix/store/5sphs39wg8gyy5pcrcml5pyj1wrn9jil-libGL-1.3.2-dev" [6]="/nix/store/q47zvd16rpppdz4ami7wrx8318fwjpfk-libGL-1.3.2" [7]="/nix/store/sv2xsv21136linyi477ijd00gmihlwbp-libglvnd-1.3.2" [8]="/nix/store/skwk7hmzwpjvhgjf1y633l01p114xc6n-libglvnd-1.3.2-dev" [9]="/nix/store/3y6j0k82fdp3ifnq655yv1ad1ih7r70j-libX11-1.7.0-dev" [10]="/nix/store/vjimy2jlifszmmms664373jdw0yiz4ln-xorgproto-2020.1" [11]="/nix/store/6xg7mhwh2k7i27r44lx679diwd7ahq6d-libxcb-1.14-dev" [12]="/nix/store/jmpwz53zajcld1xb8zslh8g8p19d8wzl-libxcb-1.14" [13]="/nix/store/80p7f1sw4szy7w1mhqxd268n6i1gmybs-libX11-1.7.0" [14]="/nix/store/k58g6biziz8vpk6zw5n9bym2c6wxjrpd-SDL2-2.0.14" [15]="/nix/store/zrh1cskgv6kc8bh7hzfii14h2nck33qv-patchelf-0.12" [16]="/nix/store/mjjy30kxz775bhhi6j9phw81qh6dsbrf-move-docs.sh" [17]="/nix/store/kxw6q8v6isaqjm702d71n2421cxamq68-make-symlinks-relative.sh" [18]="/nix/store/cl3qd985p1yxyfkj96v0hqxiy3w69xq5-compress-man-pages.sh" [19]="/nix/store/h54dzwd7rdh2jlcv91424csl6d0ccgjy-strip.sh" [20]="/nix/store/bnj8d7mvbkg3vdb07yz74yhl3g107qq5-patch-shebangs.sh" [21]="/nix/store/cickvswrvann041nqxb0rxilc46svw1n-prune-libtool-files.sh" [22]="/nix/store/8zxndz5ag0p6s526c2xyllhk1nrn4c3i-audit-tmpdir.sh" [23]="/nix/store/c8n9kcdddp9np665xz6ri61b383nxvz8-move-systemd-user-units.sh" [24]="/nix/store/1i5y55x4b4m9qkx5dqbmr1r6bvrqbanw-multiple-outputs.sh" [25]="/nix/store/kd4xwxjpjxi71jkm6ka0np72if9rm3y0-move-sbin.sh" [26]="/nix/store/fyaryjvghbkpfnsyw97hb3lyb37s1pd6-move-lib64.sh" [27]="/nix/store/ngg1cv31c8c7bcm2n8ww4g06nq7s4zhm-set-source-date-epoch-to-latest.sh" [28]="/nix/store/pr6nzbscdpg94yvr151lrif2kg0csx7b-reproducible-builds.sh" [29]="/nix/store/ca37d3qrydh0wpw40kswsx30j8dyzxh2-gcc-wrapper-10.2.0" [30]="/nix/store/jlyaypckqlapwqpck7jscs6qcdwg9bk3-binutils-wrapper-2.35.1")
146postFixupHooks=([0]="_multioutPropagateDev")
147postUnpackHooks=([0]="_updateSourceDateEpochFromSourceRoot")
148preConfigureHooks=([0]="_multioutConfig")
149preFixupHooks=([0]="_moveToShare" [1]="_multioutDocs" [2]="_multioutDevs")
150prefix=/home/np/code/rust/sdl-tests/outputs/out
151propagatedBuildDepFiles=([0]="propagated-build-build-deps" [1]="propagated-native-build-inputs" [2]="propagated-build-target-deps")
152propagatedBuildInputs=
153export propagatedBuildInputs
154propagatedHostDepFiles=([0]="propagated-host-host-deps" [1]="propagated-build-inputs")
155propagatedNativeBuildInputs=
156export propagatedNativeBuildInputs
157propagatedTargetDepFiles=([0]="propagated-target-target-deps")
158shell=/nix/store/f7jzmxq9bpbxsg69cszx56mw14n115n5-bash-4.4-p23/bin/bash
159export shell
160shellHook=
161export shellHook
162stdenv=/nix/store/6vjmwq0nxsqm4skc8mrwif0zgy2gmn6r-stdenv-linux
163export stdenv
164strictDeps=
165export strictDeps
166system=x86_64-linux
167export system
168unpackCmdHooks=([0]="_defaultUnpack")
169PATH="$PATH:$nix_saved_PATH"
170_activatePkgs ()
171{
172 local -i hostOffset targetOffset;
173 local pkg;
174 for hostOffset in "${allPlatOffsets[@]}";
175 do
176 local pkgsVar="${pkgAccumVarVars[$hostOffset + 1]}";
177 for targetOffset in "${allPlatOffsets[@]}";
178 do
179 (( "$hostOffset" <= "$targetOffset" )) || continue;
180 local pkgsRef="${pkgsVar}[$targetOffset - $hostOffset]";
181 local pkgsSlice="${!pkgsRef}[@]";
182 for pkg in ${!pkgsSlice+"${!pkgsSlice}"};
183 do
184 activatePackage "$pkg" "$hostOffset" "$targetOffset";
185 done;
186 done;
187 done
188}
189_addRpathPrefix ()
190{
191 if [ "${NIX_NO_SELF_RPATH:-0}" != 1 ]; then
192 export NIX_LDFLAGS="-rpath $1/lib ${NIX_LDFLAGS-}";
193 if [ -n "${NIX_LIB64_IN_SELF_RPATH:-}" ]; then
194 export NIX_LDFLAGS="-rpath $1/lib64 ${NIX_LDFLAGS-}";
195 fi;
196 if [ -n "${NIX_LIB32_IN_SELF_RPATH:-}" ]; then
197 export NIX_LDFLAGS="-rpath $1/lib32 ${NIX_LDFLAGS-}";
198 fi;
199 fi
200}
201_addToEnv ()
202{
203 local -i depHostOffset depTargetOffset;
204 local pkg;
205 for depHostOffset in "${allPlatOffsets[@]}";
206 do
207 local hookVar="${pkgHookVarVars[$depHostOffset + 1]}";
208 local pkgsVar="${pkgAccumVarVars[$depHostOffset + 1]}";
209 for depTargetOffset in "${allPlatOffsets[@]}";
210 do
211 (( "$depHostOffset" <= "$depTargetOffset" )) || continue;
212 local hookRef="${hookVar}[$depTargetOffset - $depHostOffset]";
213 if [[ -z "${strictDeps-}" ]]; then
214 local visitedPkgs="";
215 for pkg in ${pkgsBuildBuild+"${pkgsBuildBuild[@]}"} ${pkgsBuildHost+"${pkgsBuildHost[@]}"} ${pkgsBuildTarget+"${pkgsBuildTarget[@]}"} ${pkgsHostHost+"${pkgsHostHost[@]}"} ${pkgsHostTarget+"${pkgsHostTarget[@]}"} ${pkgsTargetTarget+"${pkgsTargetTarget[@]}"};
216 do
217 if [[ "$visitedPkgs" = *"$pkg"* ]]; then
218 continue;
219 fi;
220 runHook "${!hookRef}" "$pkg";
221 visitedPkgs+=" $pkg";
222 done;
223 else
224 local pkgsRef="${pkgsVar}[$depTargetOffset - $depHostOffset]";
225 local pkgsSlice="${!pkgsRef}[@]";
226 for pkg in ${!pkgsSlice+"${!pkgsSlice}"};
227 do
228 runHook "${!hookRef}" "$pkg";
229 done;
230 fi;
231 done;
232 done
233}
234_allFlags ()
235{
236 for varName in $(awk 'BEGIN { for (v in ENVIRON) if (v ~ /^[a-z][a-zA-Z0-9_]*$/) print v }');
237 do
238 if (( "${NIX_DEBUG:-0}" >= 1 )); then
239 printf "@%s@ -> %q\n" "${varName}" "${!varName}";
240 fi;
241 args+=("--subst-var" "$varName");
242 done
243}
244_assignFirst ()
245{
246 local varName="$1";
247 local REMOVE=REMOVE;
248 shift;
249 while (( $# )); do
250 if [ -n "${!1-}" ]; then
251 eval "${varName}"="$1";
252 return;
253 fi;
254 shift;
255 done;
256 echo "Error: _assignFirst found no valid variant!";
257 return 1
258}
259_callImplicitHook ()
260{
261 local def="$1";
262 local hookName="$2";
263 if declare -F "$hookName" > /dev/null; then
264 "$hookName";
265 else
266 if type -p "$hookName" > /dev/null; then
267 source "$hookName";
268 else
269 if [ -n "${!hookName:-}" ]; then
270 eval "${!hookName}";
271 else
272 return "$def";
273 fi;
274 fi;
275 fi
276}
277_defaultUnpack ()
278{
279 local fn="$1";
280 if [ -d "$fn" ]; then
281 cp -pr --reflink=auto -- "$fn" "$(stripHash "$fn")";
282 else
283 case "$fn" in
284 *.tar.xz | *.tar.lzma | *.txz)
285 xz -d < "$fn" | tar xf -
286 ;;
287 *.tar | *.tar.* | *.tgz | *.tbz2 | *.tbz)
288 tar xf "$fn"
289 ;;
290 *)
291 return 1
292 ;;
293 esac;
294 fi
295}
296_doStrip ()
297{
298 local -ra flags=(dontStripHost dontStripTarget);
299 local -ra stripCmds=(STRIP TARGET_STRIP);
300 if [[ "${STRIP-}" == "${TARGET_STRIP-}" ]]; then
301 dontStripTarget+=1;
302 fi;
303 local i;
304 for i in ${!stripCmds[@]};
305 do
306 local -n flag="${flags[$i]}";
307 local -n stripCmd="${stripCmds[$i]}";
308 if [[ -n "${dontStrip-}" || -n "${flag-}" ]] || ! type -f "${stripCmd-}" 2> /dev/null; then
309 continue;
310 fi;
311 stripDebugList=${stripDebugList:-lib lib32 lib64 libexec bin sbin};
312 if [ -n "$stripDebugList" ]; then
313 stripDirs "$stripCmd" "$stripDebugList" "${stripDebugFlags:--S}";
314 fi;
315 stripAllList=${stripAllList:-};
316 if [ -n "$stripAllList" ]; then
317 stripDirs "$stripCmd" "$stripAllList" "${stripAllFlags:--s}";
318 fi;
319 done
320}
321_eval ()
322{
323 if declare -F "$1" > /dev/null 2>&1; then
324 "$@";
325 else
326 eval "$1";
327 fi
328}
329_makeSymlinksRelative ()
330{
331 local symlinkTarget;
332 if [ -n "${dontRewriteSymlinks-}" ]; then
333 return 0;
334 fi;
335 while IFS= read -r -d '' f; do
336 symlinkTarget=$(readlink "$f");
337 if [[ "$symlinkTarget"/ != "$prefix"/* ]]; then
338 continue;
339 fi;
340 if [ ! -e "$symlinkTarget" ]; then
341 echo "the symlink $f is broken, it points to $symlinkTarget (which is missing)";
342 fi;
343 echo "rewriting symlink $f to be relative to $prefix";
344 ln -snrf "$symlinkTarget" "$f";
345 done < <(find $prefix -type l -print0)
346}
347_moveLib64 ()
348{
349 if [ "${dontMoveLib64-}" = 1 ]; then
350 return;
351 fi;
352 if [ ! -e "$prefix/lib64" -o -L "$prefix/lib64" ]; then
353 return;
354 fi;
355 echo "moving $prefix/lib64/* to $prefix/lib";
356 mkdir -p $prefix/lib;
357 shopt -s dotglob;
358 for i in $prefix/lib64/*;
359 do
360 mv --no-clobber "$i" $prefix/lib;
361 done;
362 shopt -u dotglob;
363 rmdir $prefix/lib64;
364 ln -s lib $prefix/lib64
365}
366_moveSbin ()
367{
368 if [ "${dontMoveSbin-}" = 1 ]; then
369 return;
370 fi;
371 if [ ! -e "$prefix/sbin" -o -L "$prefix/sbin" ]; then
372 return;
373 fi;
374 echo "moving $prefix/sbin/* to $prefix/bin";
375 mkdir -p $prefix/bin;
376 shopt -s dotglob;
377 for i in $prefix/sbin/*;
378 do
379 mv "$i" $prefix/bin;
380 done;
381 shopt -u dotglob;
382 rmdir $prefix/sbin;
383 ln -s bin $prefix/sbin
384}
385_moveSystemdUserUnits ()
386{
387 if [ "${dontMoveSystemdUserUnits:-0}" = 1 ]; then
388 return;
389 fi;
390 if [ ! -e "${prefix:?}/lib/systemd/user" ]; then
391 return;
392 fi;
393 local source="$prefix/lib/systemd/user";
394 local target="$prefix/share/systemd/user";
395 echo "moving $source/* to $target";
396 mkdir -p "$target";
397 ( shopt -s dotglob;
398 for i in "$source"/*;
399 do
400 mv "$i" "$target";
401 done );
402 rmdir "$source";
403 ln -s "$target" "$source"
404}
405_moveToShare ()
406{
407 forceShare=${forceShare:=man doc info};
408 if [ -z "$forceShare" -o -z "$out" ]; then
409 return;
410 fi;
411 for d in $forceShare;
412 do
413 if [ -d "$out/$d" ]; then
414 if [ -d "$out/share/$d" ]; then
415 echo "both $d/ and share/$d/ exist!";
416 else
417 echo "moving $out/$d to $out/share/$d";
418 mkdir -p $out/share;
419 mv $out/$d $out/share/;
420 fi;
421 fi;
422 done
423}
424_multioutConfig ()
425{
426 if [ "$outputs" = "out" ] || [ -z "${setOutputFlags-1}" ]; then
427 return;
428 fi;
429 if [ -z "$shareDocName" ]; then
430 local confScript="$configureScript";
431 if [ -z "$confScript" ] && [ -x ./configure ]; then
432 confScript=./configure;
433 fi;
434 if [ -f "$confScript" ]; then
435 local shareDocName="$(sed -n "s/^PACKAGE_TARNAME='\(.*\)'$/\1/p" < "$confScript")";
436 fi;
437 if [ -z "$shareDocName" ] || echo "$shareDocName" | grep -q '[^a-zA-Z0-9_-]'; then
438 shareDocName="$(echo "$name" | sed 's/-[^a-zA-Z].*//')";
439 fi;
440 fi;
441 configureFlags=" --bindir=${!outputBin}/bin --sbindir=${!outputBin}/sbin --includedir=${!outputInclude}/include --oldincludedir=${!outputInclude}/include --mandir=${!outputMan}/share/man --infodir=${!outputInfo}/share/info --docdir=${!outputDoc}/share/doc/${shareDocName} --libdir=${!outputLib}/lib --libexecdir=${!outputLib}/libexec --localedir=${!outputLib}/share/locale $configureFlags";
442 installFlags=" pkgconfigdir=${!outputDev}/lib/pkgconfig m4datadir=${!outputDev}/share/aclocal aclocaldir=${!outputDev}/share/aclocal $installFlags"
443}
444_multioutDevs ()
445{
446 if [ "$outputs" = "out" ] || [ -z "${moveToDev-1}" ]; then
447 return;
448 fi;
449 moveToOutput include "${!outputInclude}";
450 moveToOutput lib/pkgconfig "${!outputDev}";
451 moveToOutput share/pkgconfig "${!outputDev}";
452 moveToOutput lib/cmake "${!outputDev}";
453 moveToOutput share/aclocal "${!outputDev}";
454 for f in "${!outputDev}"/{lib,share}/pkgconfig/*.pc;
455 do
456 echo "Patching '$f' includedir to output ${!outputInclude}";
457 sed -i "/^includedir=/s,=\${prefix},=${!outputInclude}," "$f";
458 done
459}
460_multioutDocs ()
461{
462 local REMOVE=REMOVE;
463 moveToOutput share/info "${!outputInfo}";
464 moveToOutput share/doc "${!outputDoc}";
465 moveToOutput share/gtk-doc "${!outputDevdoc}";
466 moveToOutput share/devhelp/books "${!outputDevdoc}";
467 moveToOutput share/man "${!outputMan}";
468 moveToOutput share/man/man3 "${!outputDevman}"
469}
470_multioutPropagateDev ()
471{
472 if [ "$outputs" = "out" ]; then
473 return;
474 fi;
475 local outputFirst;
476 for outputFirst in $outputs;
477 do
478 break;
479 done;
480 local propagaterOutput="$outputDev";
481 if [ -z "$propagaterOutput" ]; then
482 propagaterOutput="$outputFirst";
483 fi;
484 if [ -z "${propagatedBuildOutputs+1}" ]; then
485 local po_dirty="$outputBin $outputInclude $outputLib";
486 set +o pipefail;
487 propagatedBuildOutputs=`echo "$po_dirty" | tr -s ' ' '\n' | grep -v -F "$propagaterOutput" | sort -u | tr '\n' ' ' `;
488 set -o pipefail;
489 fi;
490 if [ -z "$propagatedBuildOutputs" ]; then
491 return;
492 fi;
493 mkdir -p "${!propagaterOutput}"/nix-support;
494 for output in $propagatedBuildOutputs;
495 do
496 echo -n " ${!output}" >> "${!propagaterOutput}"/nix-support/propagated-build-inputs;
497 done
498}
499_overrideFirst ()
500{
501 if [ -z "${!1-}" ]; then
502 _assignFirst "$@";
503 fi
504}
505_pruneLibtoolFiles ()
506{
507 if [ "${dontPruneLibtoolFiles-}" ] || [ ! -e "$prefix" ]; then
508 return;
509 fi;
510 find "$prefix" -type f -name '*.la' -exec grep -q '^# Generated by .*libtool' {} \; -exec grep -q "^old_library=''" {} \; -exec sed -i {} -e "/^dependency_libs='[^']/ c dependency_libs='' #pruned" \;
511}
512_updateSourceDateEpochFromSourceRoot ()
513{
514 if [ -n "$sourceRoot" ]; then
515 updateSourceDateEpoch "$sourceRoot";
516 fi
517}
518activatePackage ()
519{
520 local pkg="$1";
521 local -ri hostOffset="$2";
522 local -ri targetOffset="$3";
523 (( "$hostOffset" <= "$targetOffset" )) || exit -1;
524 if [ -f "$pkg" ]; then
525 source "$pkg";
526 fi;
527 if [[ -z "${strictDeps-}" || "$hostOffset" -le -1 ]]; then
528 addToSearchPath _PATH "$pkg/bin";
529 fi;
530 if [[ "$hostOffset" -le -1 ]]; then
531 addToSearchPath _XDG_DATA_DIRS "$pkg/share";
532 fi;
533 if [[ "$hostOffset" -eq 0 && -d "$pkg/bin" ]]; then
534 addToSearchPath _HOST_PATH "$pkg/bin";
535 fi;
536 if [[ -f "$pkg/nix-support/setup-hook" ]]; then
537 source "$pkg/nix-support/setup-hook";
538 fi
539}
540addEnvHooks ()
541{
542 local depHostOffset="$1";
543 shift;
544 local pkgHookVarsSlice="${pkgHookVarVars[$depHostOffset + 1]}[@]";
545 local pkgHookVar;
546 for pkgHookVar in "${!pkgHookVarsSlice}";
547 do
548 eval "${pkgHookVar}s"'+=("$@")';
549 done
550}
551addSDL2Path ()
552{
553 if [ -e "$1/include/SDL2" ]; then
554 export SDL2_PATH="${SDL2_PATH-}${SDL2_PATH:+ }$1/include/SDL2";
555 fi
556}
557addToSearchPath ()
558{
559 addToSearchPathWithCustomDelimiter ":" "$@"
560}
561addToSearchPathWithCustomDelimiter ()
562{
563 local delimiter="$1";
564 local varName="$2";
565 local dir="$3";
566 if [ -d "$dir" ]; then
567 export "${varName}=${!varName:+${!varName}${delimiter}}${dir}";
568 fi
569}
570auditTmpdir ()
571{
572 local dir="$1";
573 [ -e "$dir" ] || return 0;
574 header "checking for references to $TMPDIR/ in $dir...";
575 local i;
576 while IFS= read -r -d '' i; do
577 if [[ "$i" =~ .build-id ]]; then
578 continue;
579 fi;
580 if isELF "$i"; then
581 if {
582 printf :;
583 patchelf --print-rpath "$i"
584 } | grep -q -F ":$TMPDIR/"; then
585 echo "RPATH of binary $i contains a forbidden reference to $TMPDIR/";
586 exit 1;
587 fi;
588 fi;
589 if isScript "$i"; then
590 if [ -e "$(dirname "$i")/.$(basename "$i")-wrapped" ]; then
591 if grep -q -F "$TMPDIR/" "$i"; then
592 echo "wrapper script $i contains a forbidden reference to $TMPDIR/";
593 exit 1;
594 fi;
595 fi;
596 fi;
597 done < <(find "$dir" -type f -print0);
598 stopNest
599}
600bintoolsWrapper_addLDVars ()
601{
602 local role_post;
603 getHostRoleEnvHook;
604 if [[ -d "$1/lib64" && ! -L "$1/lib64" ]]; then
605 export NIX_LDFLAGS${role_post}+=" -L$1/lib64";
606 fi;
607 if [[ -d "$1/lib" ]]; then
608 local -a glob=($1/lib/lib*);
609 if [ "${#glob[*]}" -gt 0 ]; then
610 export NIX_LDFLAGS${role_post}+=" -L$1/lib";
611 fi;
612 fi
613}
614buildPhase ()
615{
616 runHook preBuild;
617 : ${makeFlags=};
618 if [[ -z "$makeFlags" && -z "${makefile:-}" && ! ( -e Makefile || -e makefile || -e GNUmakefile ) ]]; then
619 echo "no Makefile, doing nothing";
620 else
621 foundMakefile=1;
622 local flagsArray=(${enableParallelBuilding:+-j${NIX_BUILD_CORES} -l${NIX_BUILD_CORES}} SHELL=$SHELL $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} $buildFlags ${buildFlagsArray+"${buildFlagsArray[@]}"});
623 echoCmd 'build flags' "${flagsArray[@]}";
624 make ${makefile:+-f $makefile} "${flagsArray[@]}";
625 unset flagsArray;
626 fi;
627 runHook postBuild
628}
629ccWrapper_addCVars ()
630{
631 local role_post;
632 getHostRoleEnvHook;
633 if [ -d "$1/include" ]; then
634 export NIX_CFLAGS_COMPILE${role_post}+=" -isystem $1/include";
635 fi;
636 if [ -d "$1/Library/Frameworks" ]; then
637 export NIX_CFLAGS_COMPILE${role_post}+=" -iframework $1/Library/Frameworks";
638 fi
639}
640checkPhase ()
641{
642 runHook preCheck;
643 if [[ -z "${foundMakefile:-}" ]]; then
644 echo "no Makefile or custom checkPhase, doing nothing";
645 runHook postCheck;
646 return;
647 fi;
648 if [[ -z "${checkTarget:-}" ]]; then
649 if make -n ${makefile:+-f $makefile} check > /dev/null 2>&1; then
650 checkTarget=check;
651 else
652 if make -n ${makefile:+-f $makefile} test > /dev/null 2>&1; then
653 checkTarget=test;
654 fi;
655 fi;
656 fi;
657 if [[ -z "${checkTarget:-}" ]]; then
658 echo "no check/test target in ${makefile:-Makefile}, doing nothing";
659 else
660 local flagsArray=(${enableParallelChecking:+-j${NIX_BUILD_CORES} -l${NIX_BUILD_CORES}} SHELL=$SHELL $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} ${checkFlags:-VERBOSE=y} ${checkFlagsArray+"${checkFlagsArray[@]}"} ${checkTarget});
661 echoCmd 'check flags' "${flagsArray[@]}";
662 make ${makefile:+-f $makefile} "${flagsArray[@]}";
663 unset flagsArray;
664 fi;
665 runHook postCheck
666}
667closeNest ()
668{
669 true
670}
671compressManPages ()
672{
673 local dir="$1";
674 if [ -L "$dir"/share ] || [ -L "$dir"/share/man ] || [ ! -d "$dir/share/man" ]; then
675 return;
676 fi;
677 echo "gzipping man pages under $dir/share/man/";
678 find "$dir"/share/man/ -type f -a '!' -regex '.*\.\(bz2\|gz\)$' -print0 | while IFS= read -r -d '' f; do
679 if gzip -c -n "$f" > "$f".gz; then
680 rm "$f";
681 else
682 rm "$f".gz;
683 fi;
684 done;
685 find "$dir"/share/man/ -type l -a '!' -regex '.*\.\(bz2\|gz\)$' -print0 | sort -z | while IFS= read -r -d '' f; do
686 local target;
687 target="$(readlink -f "$f")";
688 if [ -f "$target".gz ]; then
689 ln -sf "$target".gz "$f".gz && rm "$f";
690 fi;
691 done
692}
693configurePhase ()
694{
695 runHook preConfigure;
696 : ${configureScript=};
697 : ${configureFlags=};
698 if [[ -z "$configureScript" && -x ./configure ]]; then
699 configureScript=./configure;
700 fi;
701 if [ -z "${dontFixLibtool:-}" ]; then
702 local i;
703 find . -iname "ltmain.sh" -print0 | while IFS='' read -r -d '' i; do
704 echo "fixing libtool script $i";
705 fixLibtool "$i";
706 done;
707 fi;
708 if [[ -z "${dontAddPrefix:-}" && -n "$prefix" ]]; then
709 configureFlags="${prefixKey:---prefix=}$prefix $configureFlags";
710 fi;
711 if [ -z "${dontAddDisableDepTrack:-}" ]; then
712 if [ -f "$configureScript" ] && grep -q dependency-tracking "$configureScript"; then
713 configureFlags="--disable-dependency-tracking $configureFlags";
714 fi;
715 fi;
716 if [ -z "${dontDisableStatic:-}" ]; then
717 if [ -f "$configureScript" ] && grep -q enable-static "$configureScript"; then
718 configureFlags="--disable-static $configureFlags";
719 fi;
720 fi;
721 if [ -n "$configureScript" ]; then
722 local flagsArray=($configureFlags ${configureFlagsArray+"${configureFlagsArray[@]}"});
723 echoCmd 'configure flags' "${flagsArray[@]}";
724 $configureScript "${flagsArray[@]}";
725 unset flagsArray;
726 else
727 echo "no configure script, doing nothing";
728 fi;
729 runHook postConfigure
730}
731consumeEntire ()
732{
733 if IFS='' read -r -N 0 $1; then
734 echo "consumeEntire(): ERROR: Input null bytes, won't process" 1>&2;
735 return 1;
736 fi
737}
738distPhase ()
739{
740 runHook preDist;
741 local flagsArray=($distFlags ${distFlagsArray+"${distFlagsArray[@]}"} ${distTarget:-dist});
742 echo 'dist flags: %q' "${flagsArray[@]}";
743 make ${makefile:+-f $makefile} "${flagsArray[@]}";
744 if [ "${dontCopyDist:-0}" != 1 ]; then
745 mkdir -p "$out/tarballs";
746 cp -pvd ${tarballs:-*.tar.gz} "$out/tarballs";
747 fi;
748 runHook postDist
749}
750dumpVars ()
751{
752 if [ "${noDumpEnvVars:-0}" != 1 ]; then
753 export 2> /dev/null >| "$NIX_BUILD_TOP/env-vars" || true;
754 fi
755}
756echoCmd ()
757{
758 printf "%s:" "$1";
759 shift;
760 printf ' %q' "$@";
761 echo
762}
763exitHandler ()
764{
765 exitCode="$?";
766 set +e;
767 if [ -n "${showBuildStats:-}" ]; then
768 times > "$NIX_BUILD_TOP/.times";
769 local -a times=($(cat "$NIX_BUILD_TOP/.times"));
770 echo "build time elapsed: " "${times[@]}";
771 fi;
772 if (( "$exitCode" != 0 )); then
773 runHook failureHook;
774 if [ -n "${succeedOnFailure:-}" ]; then
775 echo "build failed with exit code $exitCode (ignored)";
776 mkdir -p "$out/nix-support";
777 printf "%s" "$exitCode" > "$out/nix-support/failed";
778 exit 0;
779 fi;
780 else
781 runHook exitHook;
782 fi;
783 exit "$exitCode"
784}
785findInputs ()
786{
787 local -r pkg="$1";
788 local -ri hostOffset="$2";
789 local -ri targetOffset="$3";
790 (( "$hostOffset" <= "$targetOffset" )) || exit -1;
791 local varVar="${pkgAccumVarVars[$hostOffset + 1]}";
792 local varRef="$varVar[\$targetOffset - \$hostOffset]";
793 local var="${!varRef}";
794 unset -v varVar varRef;
795 local varSlice="$var[*]";
796 case "${!varSlice-}" in
797 *" $pkg "*)
798 return 0
799 ;;
800 esac;
801 unset -v varSlice;
802 eval "$var"'+=("$pkg")';
803 if ! [ -e "$pkg" ]; then
804 echo "build input $pkg does not exist" 1>&2;
805 exit 1;
806 fi;
807 local -i mapOffsetResult;
808 function mapOffset ()
809 {
810 local -ri inputOffset="$1";
811 if (( "$inputOffset" <= 0 )); then
812 local -ri outputOffset="$inputOffset + $hostOffset";
813 else
814 local -ri outputOffset="$inputOffset - 1 + $targetOffset";
815 fi;
816 mapOffsetResult="$outputOffset"
817 };
818 local -i relHostOffset;
819 for relHostOffset in "${allPlatOffsets[@]}";
820 do
821 local files="${propagatedDepFilesVars[$relHostOffset + 1]}";
822 mapOffset relHostOffset;
823 local -i hostOffsetNext="$mapOffsetResult";
824 [[ "${allPlatOffsets[*]}" = *"$hostOffsetNext"* ]] || continue;
825 local -i relTargetOffset;
826 for relTargetOffset in "${allPlatOffsets[@]}";
827 do
828 (( "$relHostOffset" <= "$relTargetOffset" )) || continue;
829 local fileRef="${files}[$relTargetOffset - $relHostOffset]";
830 local file="${!fileRef}";
831 unset -v fileRef;
832 mapOffset relTargetOffset;
833 local -i targetOffsetNext="$mapOffsetResult";
834 [[ "${allPlatOffsets[*]}" = *"$targetOffsetNext"* ]] || continue;
835 [[ -f "$pkg/nix-support/$file" ]] || continue;
836 local pkgNext;
837 read -r -d '' pkgNext < "$pkg/nix-support/$file" || true;
838 for pkgNext in $pkgNext;
839 do
840 findInputs "$pkgNext" "$hostOffsetNext" "$targetOffsetNext";
841 done;
842 done;
843 done
844}
845fixLibtool ()
846{
847 sed -i -e 's^eval sys_lib_.*search_path=.*^^' "$1"
848}
849fixupPhase ()
850{
851 local output;
852 for output in $outputs;
853 do
854 if [ -e "${!output}" ]; then
855 chmod -R u+w "${!output}";
856 fi;
857 done;
858 runHook preFixup;
859 local output;
860 for output in $outputs;
861 do
862 prefix="${!output}" runHook fixupOutput;
863 done;
864 declare -ra flatVars=(depsBuildBuildPropagated propagatedNativeBuildInputs depsBuildTargetPropagated depsHostHostPropagated propagatedBuildInputs depsTargetTargetPropagated);
865 declare -ra flatFiles=("${propagatedBuildDepFiles[@]}" "${propagatedHostDepFiles[@]}" "${propagatedTargetDepFiles[@]}");
866 local propagatedInputsIndex;
867 for propagatedInputsIndex in "${!flatVars[@]}";
868 do
869 local propagatedInputsSlice="${flatVars[$propagatedInputsIndex]}[@]";
870 local propagatedInputsFile="${flatFiles[$propagatedInputsIndex]}";
871 [[ -n "${!propagatedInputsSlice}" ]] || continue;
872 mkdir -p "${!outputDev}/nix-support";
873 printWords ${!propagatedInputsSlice} > "${!outputDev}/nix-support/$propagatedInputsFile";
874 done;
875 if [ -n "${setupHook:-}" ]; then
876 mkdir -p "${!outputDev}/nix-support";
877 substituteAll "$setupHook" "${!outputDev}/nix-support/setup-hook";
878 fi;
879 if [ -n "${setupHooks:-}" ]; then
880 mkdir -p "${!outputDev}/nix-support";
881 local hook;
882 for hook in $setupHooks;
883 do
884 local content;
885 consumeEntire content < "$hook";
886 substituteAllStream content "file '$hook'" >> "${!outputDev}/nix-support/setup-hook";
887 unset -v content;
888 done;
889 unset -v hook;
890 fi;
891 if [ -n "${propagatedUserEnvPkgs:-}" ]; then
892 mkdir -p "${!outputBin}/nix-support";
893 printWords $propagatedUserEnvPkgs > "${!outputBin}/nix-support/propagated-user-env-packages";
894 fi;
895 runHook postFixup
896}
897genericBuild ()
898{
899 if [ -f "${buildCommandPath:-}" ]; then
900 source "$buildCommandPath";
901 return;
902 fi;
903 if [ -n "${buildCommand:-}" ]; then
904 eval "$buildCommand";
905 return;
906 fi;
907 if [ -z "${phases:-}" ]; then
908 phases="${prePhases:-} unpackPhase patchPhase ${preConfigurePhases:-} configurePhase ${preBuildPhases:-} buildPhase checkPhase ${preInstallPhases:-} installPhase ${preFixupPhases:-} fixupPhase installCheckPhase ${preDistPhases:-} distPhase ${postPhases:-}";
909 fi;
910 for curPhase in $phases;
911 do
912 if [[ "$curPhase" = unpackPhase && -n "${dontUnpack:-}" ]]; then
913 continue;
914 fi;
915 if [[ "$curPhase" = patchPhase && -n "${dontPatch:-}" ]]; then
916 continue;
917 fi;
918 if [[ "$curPhase" = configurePhase && -n "${dontConfigure:-}" ]]; then
919 continue;
920 fi;
921 if [[ "$curPhase" = buildPhase && -n "${dontBuild:-}" ]]; then
922 continue;
923 fi;
924 if [[ "$curPhase" = checkPhase && -z "${doCheck:-}" ]]; then
925 continue;
926 fi;
927 if [[ "$curPhase" = installPhase && -n "${dontInstall:-}" ]]; then
928 continue;
929 fi;
930 if [[ "$curPhase" = fixupPhase && -n "${dontFixup:-}" ]]; then
931 continue;
932 fi;
933 if [[ "$curPhase" = installCheckPhase && -z "${doInstallCheck:-}" ]]; then
934 continue;
935 fi;
936 if [[ "$curPhase" = distPhase && -z "${doDist:-}" ]]; then
937 continue;
938 fi;
939 if [[ -n $NIX_LOG_FD ]]; then
940 echo "@nix { \"action\": \"setPhase\", \"phase\": \"$curPhase\" }" 1>&$NIX_LOG_FD;
941 fi;
942 showPhaseHeader "$curPhase";
943 dumpVars;
944 eval "${!curPhase:-$curPhase}";
945 if [ "$curPhase" = unpackPhase ]; then
946 cd "${sourceRoot:-.}";
947 fi;
948 done
949}
950getHostRole ()
951{
952 getRole "$hostOffset"
953}
954getHostRoleEnvHook ()
955{
956 getRole "$depHostOffset"
957}
958getRole ()
959{
960 case $1 in
961 -1)
962 role_post='_FOR_BUILD'
963 ;;
964 0)
965 role_post=''
966 ;;
967 1)
968 role_post='_FOR_TARGET'
969 ;;
970 *)
971 echo "binutils-wrapper-2.35.1: used as improper sort of dependency" > 2;
972 return 1
973 ;;
974 esac
975}
976getTargetRole ()
977{
978 getRole "$targetOffset"
979}
980getTargetRoleEnvHook ()
981{
982 getRole "$depTargetOffset"
983}
984getTargetRoleWrapper ()
985{
986 case $targetOffset in
987 -1)
988 export NIX_BINTOOLS_WRAPPER_TARGET_BUILD_x86_64_unknown_linux_gnu=1
989 ;;
990 0)
991 export NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu=1
992 ;;
993 1)
994 export NIX_BINTOOLS_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu=1
995 ;;
996 *)
997 echo "binutils-wrapper-2.35.1: used as improper sort of dependency" > 2;
998 return 1
999 ;;
1000 esac
1001}
1002header ()
1003{
1004 echo "$1"
1005}
1006installCheckPhase ()
1007{
1008 runHook preInstallCheck;
1009 if [[ -z "${foundMakefile:-}" ]]; then
1010 echo "no Makefile or custom installCheckPhase, doing nothing";
1011 else
1012 if [[ -z "${installCheckTarget:-}" ]] && ! make -n ${makefile:+-f $makefile} ${installCheckTarget:-installcheck} > /dev/null 2>&1; then
1013 echo "no installcheck target in ${makefile:-Makefile}, doing nothing";
1014 else
1015 local flagsArray=(${enableParallelChecking:+-j${NIX_BUILD_CORES} -l${NIX_BUILD_CORES}} SHELL=$SHELL $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} $installCheckFlags ${installCheckFlagsArray+"${installCheckFlagsArray[@]}"} ${installCheckTarget:-installcheck});
1016 echoCmd 'installcheck flags' "${flagsArray[@]}";
1017 make ${makefile:+-f $makefile} "${flagsArray[@]}";
1018 unset flagsArray;
1019 fi;
1020 fi;
1021 runHook postInstallCheck
1022}
1023installPhase ()
1024{
1025 runHook preInstall;
1026 if [ -n "$prefix" ]; then
1027 mkdir -p "$prefix";
1028 fi;
1029 local flagsArray=(SHELL=$SHELL $makeFlags ${makeFlagsArray+"${makeFlagsArray[@]}"} $installFlags ${installFlagsArray+"${installFlagsArray[@]}"} ${installTargets:-install});
1030 echoCmd 'install flags' "${flagsArray[@]}";
1031 make ${makefile:+-f $makefile} "${flagsArray[@]}";
1032 unset flagsArray;
1033 runHook postInstall
1034}
1035isELF ()
1036{
1037 local fn="$1";
1038 local fd;
1039 local magic;
1040 exec {fd}< "$fn";
1041 read -r -n 4 -u "$fd" magic;
1042 exec {fd}>&-;
1043 if [ "$magic" = 'ELF' ]; then
1044 return 0;
1045 else
1046 return 1;
1047 fi
1048}
1049isScript ()
1050{
1051 local fn="$1";
1052 local fd;
1053 local magic;
1054 exec {fd}< "$fn";
1055 read -r -n 2 -u "$fd" magic;
1056 exec {fd}>&-;
1057 if [[ "$magic" =~ \#! ]]; then
1058 return 0;
1059 else
1060 return 1;
1061 fi
1062}
1063mapOffset ()
1064{
1065 local -ri inputOffset="$1";
1066 if (( "$inputOffset" <= 0 )); then
1067 local -ri outputOffset="$inputOffset + $hostOffset";
1068 else
1069 local -ri outputOffset="$inputOffset - 1 + $targetOffset";
1070 fi;
1071 mapOffsetResult="$outputOffset"
1072}
1073moveToOutput ()
1074{
1075 local patt="$1";
1076 local dstOut="$2";
1077 local output;
1078 for output in $outputs;
1079 do
1080 if [ "${!output}" = "$dstOut" ]; then
1081 continue;
1082 fi;
1083 local srcPath;
1084 for srcPath in "${!output}"/$patt;
1085 do
1086 if [ ! -e "$srcPath" ] && [ ! -L "$srcPath" ]; then
1087 continue;
1088 fi;
1089 if [ "$dstOut" = REMOVE ]; then
1090 echo "Removing $srcPath";
1091 rm -r "$srcPath";
1092 else
1093 local dstPath="$dstOut${srcPath#${!output}}";
1094 echo "Moving $srcPath to $dstPath";
1095 if [ -d "$dstPath" ] && [ -d "$srcPath" ]; then
1096 rmdir "$srcPath" --ignore-fail-on-non-empty;
1097 if [ -d "$srcPath" ]; then
1098 mv -t "$dstPath" "$srcPath"/*;
1099 rmdir "$srcPath";
1100 fi;
1101 else
1102 mkdir -p "$(readlink -m "$dstPath/..")";
1103 mv "$srcPath" "$dstPath";
1104 fi;
1105 fi;
1106 local srcParent="$(readlink -m "$srcPath/..")";
1107 if rmdir "$srcParent"; then
1108 echo "Removing empty $srcParent/ and (possibly) its parents";
1109 rmdir -p --ignore-fail-on-non-empty "$(readlink -m "$srcParent/..")" 2> /dev/null || true;
1110 fi;
1111 done;
1112 done
1113}
1114patchELF ()
1115{
1116 local dir="$1";
1117 [ -e "$dir" ] || return 0;
1118 header "shrinking RPATHs of ELF executables and libraries in $dir";
1119 local i;
1120 while IFS= read -r -d '' i; do
1121 if [[ "$i" =~ .build-id ]]; then
1122 continue;
1123 fi;
1124 if ! isELF "$i"; then
1125 continue;
1126 fi;
1127 echo "shrinking $i";
1128 patchelf --shrink-rpath "$i" || true;
1129 done < <(find "$dir" -type f -print0);
1130 stopNest
1131}
1132patchPhase ()
1133{
1134 runHook prePatch;
1135 for i in ${patches:-};
1136 do
1137 header "applying patch $i" 3;
1138 local uncompress=cat;
1139 case "$i" in
1140 *.gz)
1141 uncompress="gzip -d"
1142 ;;
1143 *.bz2)
1144 uncompress="bzip2 -d"
1145 ;;
1146 *.xz)
1147 uncompress="xz -d"
1148 ;;
1149 *.lzma)
1150 uncompress="lzma -d"
1151 ;;
1152 esac;
1153 $uncompress < "$i" 2>&1 | patch ${patchFlags:--p1};
1154 done;
1155 runHook postPatch
1156}
1157patchShebangs ()
1158{
1159 local pathName;
1160 if [[ "$1" == "--host" ]]; then
1161 pathName=HOST_PATH;
1162 shift;
1163 else
1164 if [[ "$1" == "--build" ]]; then
1165 pathName=PATH;
1166 shift;
1167 fi;
1168 fi;
1169 echo "patching script interpreter paths in $@";
1170 local f;
1171 local oldPath;
1172 local newPath;
1173 local arg0;
1174 local args;
1175 local oldInterpreterLine;
1176 local newInterpreterLine;
1177 if [[ $# -eq 0 ]]; then
1178 echo "No arguments supplied to patchShebangs" 1>&2;
1179 return 0;
1180 fi;
1181 local f;
1182 while IFS= read -r -d '' f; do
1183 isScript "$f" || continue;
1184 read -r oldInterpreterLine < "$f";
1185 read -r oldPath arg0 args <<< "${oldInterpreterLine:2}";
1186 if [[ -z "$pathName" ]]; then
1187 if [[ -n $strictDeps && $f == "$NIX_STORE"* ]]; then
1188 pathName=HOST_PATH;
1189 else
1190 pathName=PATH;
1191 fi;
1192 fi;
1193 if [[ "$oldPath" == *"/bin/env" ]]; then
1194 if [[ $arg0 == "-"* || $arg0 == *"="* ]]; then
1195 echo "$f: unsupported interpreter directive \"$oldInterpreterLine\" (set dontPatchShebangs=1 and handle shebang patching yourself)" 1>&2;
1196 exit 1;
1197 fi;
1198 newPath="$(PATH="${!pathName}" command -v "$arg0" || true)";
1199 else
1200 if [[ -z $oldPath ]]; then
1201 oldPath="/bin/sh";
1202 fi;
1203 newPath="$(PATH="${!pathName}" command -v "$(basename "$oldPath")" || true)";
1204 args="$arg0 $args";
1205 fi;
1206 newInterpreterLine="$newPath $args";
1207 newInterpreterLine=${newInterpreterLine%${newInterpreterLine##*[![:space:]]}};
1208 if [[ -n "$oldPath" && "${oldPath:0:${#NIX_STORE}}" != "$NIX_STORE" ]]; then
1209 if [[ -n "$newPath" && "$newPath" != "$oldPath" ]]; then
1210 echo "$f: interpreter directive changed from \"$oldInterpreterLine\" to \"$newInterpreterLine\"";
1211 escapedInterpreterLine=${newInterpreterLine//\\/\\\\};
1212 timestamp=$(stat --printf "%y" "$f");
1213 sed -i -e "1 s|.*|#\!$escapedInterpreterLine|" "$f";
1214 touch --date "$timestamp" "$f";
1215 fi;
1216 fi;
1217 done < <(find "$@" -type f -perm -0100 -print0);
1218 stopNest
1219}
1220patchShebangsAuto ()
1221{
1222 if [[ -z "${dontPatchShebangs-}" && -e "$prefix" ]]; then
1223 if [[ "$output" != out && "$output" = "$outputDev" ]]; then
1224 patchShebangs --build "$prefix";
1225 else
1226 patchShebangs --host "$prefix";
1227 fi;
1228 fi
1229}
1230printLines ()
1231{
1232 (( "$#" > 0 )) || return 0;
1233 printf '%s\n' "$@"
1234}
1235printWords ()
1236{
1237 (( "$#" > 0 )) || return 0;
1238 printf '%s ' "$@"
1239}
1240runHook ()
1241{
1242 local hookName="$1";
1243 shift;
1244 local hooksSlice="${hookName%Hook}Hooks[@]";
1245 local hook;
1246 for hook in "_callImplicitHook 0 $hookName" ${!hooksSlice+"${!hooksSlice}"};
1247 do
1248 _eval "$hook" "$@";
1249 done;
1250 return 0
1251}
1252runOneHook ()
1253{
1254 local hookName="$1";
1255 shift;
1256 local hooksSlice="${hookName%Hook}Hooks[@]";
1257 local hook ret=1;
1258 for hook in "_callImplicitHook 1 $hookName" ${!hooksSlice+"${!hooksSlice}"};
1259 do
1260 if _eval "$hook" "$@"; then
1261 ret=0;
1262 break;
1263 fi;
1264 done;
1265 return "$ret"
1266}
1267showPhaseHeader ()
1268{
1269 local phase="$1";
1270 case "$phase" in
1271 unpackPhase)
1272 header "unpacking sources"
1273 ;;
1274 patchPhase)
1275 header "patching sources"
1276 ;;
1277 configurePhase)
1278 header "configuring"
1279 ;;
1280 buildPhase)
1281 header "building"
1282 ;;
1283 checkPhase)
1284 header "running tests"
1285 ;;
1286 installPhase)
1287 header "installing"
1288 ;;
1289 fixupPhase)
1290 header "post-installation fixup"
1291 ;;
1292 installCheckPhase)
1293 header "running install tests"
1294 ;;
1295 *)
1296 header "$phase"
1297 ;;
1298 esac
1299}
1300stopNest ()
1301{
1302 true
1303}
1304stripDirs ()
1305{
1306 local cmd="$1";
1307 local dirs="$2";
1308 local stripFlags="$3";
1309 local dirsNew=;
1310 local d;
1311 for d in ${dirs};
1312 do
1313 if [ -d "$prefix/$d" ]; then
1314 dirsNew="${dirsNew} $prefix/$d ";
1315 fi;
1316 done;
1317 dirs=${dirsNew};
1318 if [ -n "${dirs}" ]; then
1319 header "stripping (with command $cmd and flags $stripFlags) in$dirs";
1320 find $dirs -type f -exec $cmd $commonStripFlags $stripFlags '{}' \; 2> /dev/null;
1321 stopNest;
1322 fi
1323}
1324stripHash ()
1325{
1326 local strippedName casematchOpt=0;
1327 strippedName="$(basename -- "$1")";
1328 shopt -q nocasematch && casematchOpt=1;
1329 shopt -u nocasematch;
1330 if [[ "$strippedName" =~ ^[a-z0-9]{32}- ]]; then
1331 echo "${strippedName:33}";
1332 else
1333 echo "$strippedName";
1334 fi;
1335 if (( casematchOpt )); then
1336 shopt -s nocasematch;
1337 fi
1338}
1339substitute ()
1340{
1341 local input="$1";
1342 local output="$2";
1343 shift 2;
1344 if [ ! -f "$input" ]; then
1345 echo "substitute(): ERROR: file '$input' does not exist" 1>&2;
1346 return 1;
1347 fi;
1348 local content;
1349 consumeEntire content < "$input";
1350 if [ -e "$output" ]; then
1351 chmod +w "$output";
1352 fi;
1353 substituteStream content "file '$input'" "$@" > "$output"
1354}
1355substituteAll ()
1356{
1357 local input="$1";
1358 local output="$2";
1359 local -a args=();
1360 _allFlags;
1361 substitute "$input" "$output" "${args[@]}"
1362}
1363substituteAllInPlace ()
1364{
1365 local fileName="$1";
1366 shift;
1367 substituteAll "$fileName" "$fileName" "$@"
1368}
1369substituteAllStream ()
1370{
1371 local -a args=();
1372 _allFlags;
1373 substituteStream "$1" "$2" "${args[@]}"
1374}
1375substituteInPlace ()
1376{
1377 local fileName="$1";
1378 shift;
1379 substitute "$fileName" "$fileName" "$@"
1380}
1381substituteStream ()
1382{
1383 local var=$1;
1384 local description=$2;
1385 shift 2;
1386 while (( "$#" )); do
1387 case "$1" in
1388 --replace)
1389 pattern="$2";
1390 replacement="$3";
1391 shift 3;
1392 local savedvar;
1393 savedvar="${!var}";
1394 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}';
1395 if [ "$pattern" != "$replacement" ]; then
1396 if [ "${!var}" == "$savedvar" ]; then
1397 echo "substituteStream(): WARNING: pattern '$pattern' doesn't match anything in $description" 1>&2;
1398 fi;
1399 fi
1400 ;;
1401 --subst-var)
1402 local varName="$2";
1403 shift 2;
1404 if ! [[ "$varName" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
1405 echo "substituteStream(): ERROR: substitution variables must be valid Bash names, \"$varName\" isn't." 1>&2;
1406 return 1;
1407 fi;
1408 if [ -z ${!varName+x} ]; then
1409 echo "substituteStream(): ERROR: variable \$$varName is unset" 1>&2;
1410 return 1;
1411 fi;
1412 pattern="@$varName@";
1413 replacement="${!varName}";
1414 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}'
1415 ;;
1416 --subst-var-by)
1417 pattern="@$2@";
1418 replacement="$3";
1419 eval "$var"'=${'"$var"'//"$pattern"/"$replacement"}';
1420 shift 3
1421 ;;
1422 *)
1423 echo "substituteStream(): ERROR: Invalid command line argument: $1" 1>&2;
1424 return 1
1425 ;;
1426 esac;
1427 done;
1428 printf "%s" "${!var}"
1429}
1430unpackFile ()
1431{
1432 curSrc="$1";
1433 header "unpacking source archive $curSrc" 3;
1434 if ! runOneHook unpackCmd "$curSrc"; then
1435 echo "do not know how to unpack source archive $curSrc";
1436 exit 1;
1437 fi
1438}
1439unpackPhase ()
1440{
1441 runHook preUnpack;
1442 if [ -z "${srcs:-}" ]; then
1443 if [ -z "${src:-}" ]; then
1444 echo 'variable $src or $srcs should point to the source';
1445 exit 1;
1446 fi;
1447 srcs="$src";
1448 fi;
1449 local dirsBefore="";
1450 for i in *;
1451 do
1452 if [ -d "$i" ]; then
1453 dirsBefore="$dirsBefore $i ";
1454 fi;
1455 done;
1456 for i in $srcs;
1457 do
1458 unpackFile "$i";
1459 done;
1460 : ${sourceRoot=};
1461 if [ -n "${setSourceRoot:-}" ]; then
1462 runOneHook setSourceRoot;
1463 else
1464 if [ -z "$sourceRoot" ]; then
1465 for i in *;
1466 do
1467 if [ -d "$i" ]; then
1468 case $dirsBefore in
1469 *\ $i\ *)
1470
1471 ;;
1472 *)
1473 if [ -n "$sourceRoot" ]; then
1474 echo "unpacker produced multiple directories";
1475 exit 1;
1476 fi;
1477 sourceRoot="$i"
1478 ;;
1479 esac;
1480 fi;
1481 done;
1482 fi;
1483 fi;
1484 if [ -z "$sourceRoot" ]; then
1485 echo "unpacker appears to have produced no directories";
1486 exit 1;
1487 fi;
1488 echo "source root is $sourceRoot";
1489 if [ "${dontMakeSourcesWritable:-0}" != 1 ]; then
1490 chmod -R u+w -- "$sourceRoot";
1491 fi;
1492 runHook postUnpack
1493}
1494updateSourceDateEpoch ()
1495{
1496 local path="$1";
1497 local -a res=($(find "$path" -type f -not -newer "$NIX_BUILD_TOP/.." -printf '%T@ %p\0' | sort -n --zero-terminated | tail -n1 --zero-terminated | head -c -1));
1498 local time="${res[0]//\.[0-9]*/}";
1499 local newestFile="${res[1]}";
1500 if [ "${time:-0}" -gt "$SOURCE_DATE_EPOCH" ]; then
1501 echo "setting SOURCE_DATE_EPOCH to timestamp $time of file $newestFile";
1502 export SOURCE_DATE_EPOCH="$time";
1503 local now="$(date +%s)";
1504 if [ "$time" -gt $((now - 60)) ]; then
1505 echo "warning: file $newestFile may be generated; SOURCE_DATE_EPOCH may be non-deterministic";
1506 fi;
1507 fi
1508}
1509
1510export NIX_BUILD_TOP="$(mktemp -d --tmpdir nix-shell.XXXXXX)"
1511export TMP="$NIX_BUILD_TOP"
1512export TMPDIR="$NIX_BUILD_TOP"
1513export TEMP="$NIX_BUILD_TOP"
1514export TEMPDIR="$NIX_BUILD_TOP"
1515eval "$shellHook"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..4ab04ea
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,61 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3[[package]]
4name = "bitflags"
5version = "1.2.1"
6source = "registry+https://github.com/rust-lang/crates.io-index"
7checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
8
9[[package]]
10name = "cfg-if"
11version = "0.1.10"
12source = "registry+https://github.com/rust-lang/crates.io-index"
13checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
14
15[[package]]
16name = "lazy_static"
17version = "1.4.0"
18source = "registry+https://github.com/rust-lang/crates.io-index"
19checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
20
21[[package]]
22name = "libc"
23version = "0.2.88"
24source = "registry+https://github.com/rust-lang/crates.io-index"
25checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
26
27[[package]]
28name = "sdl-tests"
29version = "0.1.0"
30dependencies = [
31 "sdl2",
32]
33
34[[package]]
35name = "sdl2"
36version = "0.34.3"
37source = "registry+https://github.com/rust-lang/crates.io-index"
38checksum = "fcbb85f4211627a7291c83434d6bbfa723e28dcaa53c7606087e3c61929e4b9c"
39dependencies = [
40 "bitflags",
41 "lazy_static",
42 "libc",
43 "sdl2-sys",
44]
45
46[[package]]
47name = "sdl2-sys"
48version = "0.34.3"
49source = "registry+https://github.com/rust-lang/crates.io-index"
50checksum = "28d81feded049b9c14eceb4a4f6d596a98cebbd59abdba949c5552a015466d33"
51dependencies = [
52 "cfg-if",
53 "libc",
54 "version-compare",
55]
56
57[[package]]
58name = "version-compare"
59version = "0.0.10"
60source = "registry+https://github.com/rust-lang/crates.io-index"
61checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..df04cd4
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,10 @@
1[package]
2name = "sdl-tests"
3version = "0.1.0"
4authors = ["Akshay <[email protected]>"]
5edition = "2018"
6
7# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
9[dependencies]
10sdl2 = "0.34"
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..3bb4565
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,91 @@
1{
2 "nodes": {
3 "mozillapkgs": {
4 "flake": false,
5 "locked": {
6 "lastModified": 1603906276,
7 "narHash": "sha256-RsNPnEKd7BcogwkqhaV5kI/HuNC4flH/OQCC/4W5y/8=",
8 "owner": "mozilla",
9 "repo": "nixpkgs-mozilla",
10 "rev": "8c007b60731c07dd7a052cce508de3bb1ae849b4",
11 "type": "github"
12 },
13 "original": {
14 "owner": "mozilla",
15 "repo": "nixpkgs-mozilla",
16 "type": "github"
17 }
18 },
19 "naersk": {
20 "inputs": {
21 "nixpkgs": "nixpkgs"
22 },
23 "locked": {
24 "lastModified": 1614785451,
25 "narHash": "sha256-TPw8kQvr2UNCuvndtY+EjyXp6Q5GEW2l9UafXXh1XmI=",
26 "owner": "nmattia",
27 "repo": "naersk",
28 "rev": "e0fe990b478a66178a58c69cf53daec0478ca6f9",
29 "type": "github"
30 },
31 "original": {
32 "owner": "nmattia",
33 "repo": "naersk",
34 "type": "github"
35 }
36 },
37 "nixpkgs": {
38 "locked": {
39 "lastModified": 1615043955,
40 "narHash": "sha256-foTwGGoArFbXdI9Pgq75is7WAqGM821dMsBuEqmSCtw=",
41 "owner": "NixOS",
42 "repo": "nixpkgs",
43 "rev": "d80850806d60e62c2a8189bc62e373a3facae442",
44 "type": "github"
45 },
46 "original": {
47 "id": "nixpkgs",
48 "type": "indirect"
49 }
50 },
51 "nixpkgs_2": {
52 "locked": {
53 "lastModified": 1615043955,
54 "narHash": "sha256-foTwGGoArFbXdI9Pgq75is7WAqGM821dMsBuEqmSCtw=",
55 "owner": "NixOS",
56 "repo": "nixpkgs",
57 "rev": "d80850806d60e62c2a8189bc62e373a3facae442",
58 "type": "github"
59 },
60 "original": {
61 "id": "nixpkgs",
62 "type": "indirect"
63 }
64 },
65 "root": {
66 "inputs": {
67 "mozillapkgs": "mozillapkgs",
68 "naersk": "naersk",
69 "nixpkgs": "nixpkgs_2",
70 "utils": "utils"
71 }
72 },
73 "utils": {
74 "locked": {
75 "lastModified": 1614513358,
76 "narHash": "sha256-LakhOx3S1dRjnh0b5Dg3mbZyH0ToC9I8Y2wKSkBaTzU=",
77 "owner": "numtide",
78 "repo": "flake-utils",
79 "rev": "5466c5bbece17adaab2d82fae80b46e807611bf3",
80 "type": "github"
81 },
82 "original": {
83 "owner": "numtide",
84 "repo": "flake-utils",
85 "type": "github"
86 }
87 }
88 },
89 "root": "root",
90 "version": 7
91}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..19aa50a
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,54 @@
1{
2 inputs = {
3 utils.url = "github:numtide/flake-utils";
4 naersk.url = "github:nmattia/naersk";
5 mozillapkgs = {
6 url = "github:mozilla/nixpkgs-mozilla";
7 flake = false;
8 };
9 };
10
11 outputs = { self, nixpkgs, utils, naersk, mozillapkgs }:
12 utils.lib.eachDefaultSystem (system: let
13 pkgs = nixpkgs.legacyPackages."${system}";
14
15 # Get a specific rust version
16 mozilla = pkgs.callPackage (mozillapkgs + "/package-set.nix") {};
17 rust = (mozilla.rustChannelOf {
18 date = "2020-12-23";
19 channel = "nightly";
20 sha256 = "LbKHsCOFXWpg/SEyACfzZuWjKbkXdH6EJKOPSGoO01E="; # set zeros after modifying channel or date
21 }).rust;
22 rust-src = (mozilla.rustChannelOf {
23 date = "2020-12-23";
24 channel = "nightly";
25 sha256 = "LbKHsCOFXWpg/SEyACfzZuWjKbkXdH6EJKOPSGoO01E="; # set zeros after modifying channel or date
26 }).rust-src;
27
28 naersk-lib = naersk.lib."${system}".override {
29 cargo = rust;
30 rustc = rust;
31 };
32 in rec {
33 packages.my-project = naersk-lib.buildPackage {
34 pname = "sdl-tests";
35 version = "0.1.0";
36 root = ./.;
37 };
38 defaultPackage = packages.my-project;
39 apps.my-project = utils.lib.mkApp {
40 drv = packages.my-project;
41 };
42 defaultApp = apps.my-project;
43 devShell = pkgs.mkShell {
44 nativeBuildInputs = [
45 rust
46 rust-src
47 pkgs.rust-analyzer
48 pkgs.cargo
49 pkgs.SDL2
50 ];
51 RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
52 };
53 });
54 }
diff --git a/src/app.rs b/src/app.rs
new file mode 100644
index 0000000..84d884f
--- /dev/null
+++ b/src/app.rs
@@ -0,0 +1,447 @@
1use crate::undo::{ModifyRecord, OpKind, Operation, UndoStack};
2
3use sdl2::{
4 event::Event,
5 keyboard::Keycode,
6 mouse::MouseButton,
7 pixels::Color,
8 rect::{Point, Rect},
9 render::Canvas,
10 video::Window,
11 Sdl,
12};
13
14use crate::consts::{BLACK, GRID_COLOR, WHITE};
15
16pub struct AppState<'ctx> {
17 start: Point,
18 width: u32,
19 height: u32,
20 data: Vec<bool>,
21 zoom: u8,
22 brush_size: u8,
23 grid: Grid,
24 context: &'ctx Sdl,
25 canvas: Canvas<Window>,
26 last_point: Option<Point>,
27 active_color: bool,
28 undo_stack: UndoStack<Operation>,
29 current_operation: Operation,
30}
31
32struct Grid {
33 enabled: bool,
34 color: Color,
35}
36
37impl Grid {
38 fn new() -> Self {
39 Self {
40 enabled: true,
41 color: GRID_COLOR,
42 }
43 }
44}
45
46// private actions on appstate
47impl<'ctx> AppState<'ctx> {
48 fn pan<P: Into<Point>>(&mut self, direction: P) {
49 self.start += direction.into();
50 }
51
52 fn bounds(&self) -> (Point, Point) {
53 let x_min = self.start.x();
54 let y_min = self.start.y();
55 let x_max = self.start.x() + (self.width * self.zoom as u32) as i32;
56 let y_max = self.start.y() + (self.height * self.zoom as u32) as i32;
57 return (
58 Point::new(x_min, y_min),
59 Point::new(x_max as i32, y_max as i32),
60 );
61 }
62
63 fn change_active_color(&mut self) {
64 self.active_color = !self.active_color;
65 }
66
67 fn idx_at_coord<P: Into<Point>>(&self, p: P) -> Option<(u32, u32)> {
68 let p: Point = p.into();
69 if self.within_canvas(p) {
70 // convert p relative to start of drawing area
71 let rel_p = p - self.start;
72 // reduce p based on zoom and cell size
73 let (sx, sy) = (rel_p.x() / self.zoom as i32, rel_p.y() / self.zoom as i32);
74 return Some((sx as u32, sy as u32));
75 } else {
76 None
77 }
78 }
79
80 fn within_canvas<P: Into<Point>>(&self, p: P) -> bool {
81 let p: Point = p.into();
82 let (mini, maxi) = self.bounds();
83 p.x() < maxi.x() && p.y() < maxi.y() && p.x() >= mini.x() && p.y() >= mini.y()
84 }
85
86 fn within_grid<P: Into<Point>>(&self, p: P) -> bool {
87 let p = p.into();
88 let (x, y) = (p.x(), p.y());
89 x >= 0 && x < self.width as i32 && y >= 0 && y < self.height as i32
90 }
91
92 fn set_at<P: Into<Point>>(&mut self, p: P, val: bool) -> Result<ModifyRecord, ()> {
93 let p: Point = p.into();
94 if let Some((x, y)) = self.idx_at_coord(p) {
95 let old_val = self.data[(y * self.width + x) as usize];
96 self.data[(y * self.width + x) as usize] = val;
97 return Ok(ModifyRecord::new((x as i32, y as i32), old_val, val));
98 }
99 return Err(());
100 }
101
102 fn set_with_absolute<P: Into<Point>>(&mut self, p: P, val: bool) -> Result<ModifyRecord, ()> {
103 let p: Point = p.into();
104 let (x, y) = (p.x(), p.y());
105 if self.within_grid(p) {
106 let idx = y as u32 * self.width + x as u32;
107 let old_val = self.data[idx as usize];
108 self.data[idx as usize] = val;
109 return Ok(ModifyRecord::new((x as i32, y as i32), old_val, val));
110 }
111 return Err(());
112 }
113
114 fn toggle_grid(&mut self) {
115 self.grid.enabled = !self.grid.enabled;
116 }
117
118 fn paint_point<P: Into<Point>>(
119 &mut self,
120 center: P,
121 val: bool,
122 ) -> Result<Vec<ModifyRecord>, ()> {
123 let radius = self.brush_size;
124 if radius == 1 {
125 return Ok(self.set_at(center, val).map(|x| vec![x])?);
126 } else {
127 if let Some(center_on_grid) = self.idx_at_coord(center) {
128 // center_on_grid is now a coordinate on the drawing grid
129 let (x0, y0) = (center_on_grid.0 as i64, center_on_grid.1 as i64);
130 let (mut dx, mut dy, mut err) = (radius as i64, 0i64, 1 - radius as i64);
131 let mut circle = vec![];
132 let mut old_vals = vec![];
133 while dx >= dy {
134 circle.push((x0 + dx, y0 + dy));
135 circle.push((x0 - dx, y0 + dy));
136 circle.push((x0 + dx, y0 - dy));
137 circle.push((x0 - dx, y0 - dy));
138 circle.push((x0 + dy, y0 + dx));
139 circle.push((x0 - dy, y0 + dx));
140 circle.push((x0 + dy, y0 - dx));
141 circle.push((x0 - dy, y0 - dx));
142 dy = dy + 1;
143 if err < 0 {
144 err = err + 2 * dy + 1;
145 } else {
146 dx -= 1;
147 err += 2 * (dy - dx) + 1;
148 }
149 }
150 // circle's insides
151 for x in 0..radius as i64 {
152 for y in 0..radius as i64 {
153 if x.pow(2) + y.pow(2) < (radius as i64).pow(2) {
154 circle.push((x0 + x, y0 + y));
155 circle.push((x0 - x, y0 + y));
156 circle.push((x0 + x, y0 - y));
157 circle.push((x0 - x, y0 - y));
158 }
159 }
160 }
161 dbg!(&circle);
162 for (x, y) in circle {
163 if self.within_grid((x as i32, y as i32)) {
164 let idx = y as u32 * self.width + x as u32;
165 old_vals.push(ModifyRecord::new(
166 (x as i32, y as i32),
167 self.data[idx as usize],
168 val,
169 ));
170 self.data[idx as usize] = val;
171 }
172 }
173 return Ok(old_vals);
174 }
175 }
176 return Err(());
177 }
178
179 fn draw_line<P: Into<Point>>(&mut self, to: P) {
180 let to = to.into();
181 let from = self.last_point.unwrap_or(to.into());
182 }
183
184 fn apply_operation(&mut self, op: Operation, op_kind: OpKind) -> Result<(), ()> {
185 for ModifyRecord {
186 point,
187 old_val,
188 val,
189 } in op.into_iter()
190 {
191 self.set_with_absolute(
192 point,
193 match op_kind {
194 OpKind::Undo => old_val,
195 OpKind::Redo => val,
196 },
197 )?;
198 }
199 Ok(())
200 }
201
202 fn zoom_in(&mut self, p: (i32, i32)) {
203 // attempt to center around cursor
204 if let Some(p) = self.idx_at_coord(p) {
205 let (x1, y1) = (p.0 * (self.zoom as u32), p.1 * (self.zoom as u32));
206 let (x2, y2) = (p.0 * (1 + self.zoom as u32), p.1 * (1 + self.zoom as u32));
207 let diffx = x2 as i32 - x1 as i32;
208 let diffy = y2 as i32 - y1 as i32;
209 self.start = self.start - Point::from((diffx, diffy));
210 }
211 self.zoom += 1;
212 }
213
214 fn increase_brush_size(&mut self) {
215 self.brush_size += 1;
216 }
217
218 fn descrease_brush_size(&mut self) {
219 if self.brush_size > 1 {
220 self.brush_size -= 1;
221 }
222 }
223
224 fn zoom_out(&mut self, p: (i32, i32)) {
225 if self.zoom > 1 {
226 // attempt to center around cursor
227 if let Some(p) = self.idx_at_coord(p) {
228 let (x1, y1) = (p.0 * (self.zoom as u32), p.1 * (self.zoom as u32));
229 let (x2, y2) = (p.0 * (self.zoom as u32 - 1), p.1 * (self.zoom as u32 - 1));
230 let diffx = x2 as i32 - x1 as i32;
231 let diffy = y2 as i32 - y1 as i32;
232 self.start = self.start - Point::from((diffx, diffy));
233 }
234 self.zoom -= 1;
235 }
236 }
237
238 fn draw_grid(&mut self) {
239 let cs = self.zoom as u32;
240 let canvas = &mut self.canvas;
241 canvas.set_draw_color(self.grid.color);
242 for i in 0..=self.width {
243 let x = (i * cs) as i32;
244 let y = (self.height * cs) as i32;
245 let start = self.start + Point::new(x, 0);
246 let end = self.start + Point::new(x, y);
247 canvas.draw_line(start, end).unwrap();
248 }
249 for j in 0..=self.height {
250 let x = (self.width * cs) as i32;
251 let y = (j * cs) as i32;
252 let start = self.start + Point::new(0, y);
253 let end = self.start + Point::new(x, y);
254 canvas.draw_line(start, end).unwrap();
255 }
256 }
257
258 fn draw(&mut self) {
259 let cs = self.zoom as u32;
260 if self.grid.enabled {
261 self.draw_grid();
262 }
263 let canvas = &mut self.canvas;
264 for (idx, val) in self.data.iter().enumerate() {
265 if *val {
266 let idx = idx as i32;
267 let (x, y) = (idx % self.width as i32, idx / self.height as i32);
268 canvas.set_draw_color(WHITE);
269 canvas
270 .fill_rect(Rect::new(
271 // start drawing 1 pixel after the grid line
272 x * cs as i32 + self.start.x() + 1,
273 y * cs as i32 + self.start.y() + 1,
274 // stop drawing 1 pixel before the grid line
275 cs - 1,
276 cs - 1,
277 ))
278 .unwrap();
279 }
280 }
281 }
282
283 fn modify<F>(&mut self, func: F)
284 where
285 F: FnOnce(&mut Self),
286 {
287 func(self);
288 self.canvas.set_draw_color(Color::RGB(0, 0, 0));
289 self.canvas.clear();
290 self.canvas.set_draw_color(Color::RGB(64, 64, 64));
291 self.draw();
292 self.canvas.present();
293 }
294}
295
296// publicly available functions on appstate
297impl<'ctx> AppState<'ctx> {
298 pub fn init(width: u32, height: u32, context: &'ctx Sdl) -> Self {
299 let video_subsystem = context.video().unwrap();
300
301 let window = video_subsystem
302 .window("Pixel editor", 200, 200)
303 .position_centered()
304 .opengl()
305 .build()
306 .map_err(|e| e.to_string())
307 .unwrap();
308
309 let canvas = window
310 .into_canvas()
311 .build()
312 .map_err(|e| e.to_string())
313 .unwrap();
314
315 let data = vec![false; (width * height) as usize];
316 Self {
317 start: Point::new(60, 60),
318 width,
319 height,
320 data,
321 zoom: 5,
322 brush_size: 1,
323 grid: Grid::new(),
324 canvas,
325 context,
326 last_point: None,
327 active_color: true,
328 undo_stack: UndoStack::new(),
329 current_operation: Vec::new(),
330 }
331 }
332
333 pub fn run(&mut self) {
334 self.canvas.set_draw_color(BLACK);
335 self.canvas.clear();
336 self.draw();
337 self.canvas.present();
338
339 let mut event_pump = self.context.event_pump().unwrap();
340
341 'running: loop {
342 let mouse = event_pump.mouse_state();
343 for event in event_pump.poll_iter() {
344 match event {
345 Event::KeyDown {
346 keycode: Some(k), ..
347 } => {
348 match k {
349 // pan
350 Keycode::W => self.modify(|e| e.pan((0, 10))),
351 Keycode::A => self.modify(|e| e.pan((10, 0))),
352 Keycode::S => self.modify(|e| e.pan((0, -10))),
353 Keycode::D => self.modify(|e| e.pan((-10, 0))),
354 // zoom
355 Keycode::C => {
356 let cursor = (mouse.x(), mouse.y());
357 self.modify(|e| e.zoom_in(cursor));
358 }
359 Keycode::Z => {
360 let cursor = (mouse.x(), mouse.y());
361 self.modify(|e| e.zoom_out(cursor));
362 }
363 // brush ops
364 Keycode::Q => self.modify(|e| e.descrease_brush_size()),
365 Keycode::E => self.modify(|e| e.increase_brush_size()),
366 // flip color
367 Keycode::X => self.modify(|e| e.change_active_color()),
368 // toggle grid
369 Keycode::Tab => self.modify(|e| e.toggle_grid()),
370 // exit
371 Keycode::Escape => break 'running,
372 // undo & redo
373 Keycode::U => self.modify(|e| {
374 if let Some(op) = e.undo_stack.undo() {
375 e.apply_operation(op, OpKind::Undo);
376 }
377 }),
378 Keycode::R => self.modify(|e| {
379 if let Some(op) = e.undo_stack.redo() {
380 e.apply_operation(op, OpKind::Redo);
381 }
382 }),
383 _ => (),
384 }
385 }
386 // start of operation
387 Event::MouseButtonDown {
388 x, y, mouse_btn, ..
389 } => {
390 self.modify(|e| {
391 let pt = (x, y);
392 e.last_point = Some(pt.into());
393 let val = match mouse_btn {
394 MouseButton::Right => !e.active_color,
395 _ => e.active_color,
396 };
397 if let Ok(o) = e.paint_point(pt, val) {
398 e.current_operation.extend(o);
399 }
400 });
401 }
402 // click and drag
403 Event::MouseMotion {
404 x, y, mousestate, ..
405 } => {
406 let is_left = mousestate.is_mouse_button_pressed(MouseButton::Left);
407 let is_right = mousestate.is_mouse_button_pressed(MouseButton::Right);
408 if is_left {
409 self.modify(|e| {
410 let pt = (x, y);
411 let val = e.active_color;
412 if let Ok(o) = e.paint_point(pt, val) {
413 e.current_operation.extend(o);
414 }
415 });
416 } else if is_right {
417 self.modify(|e| {
418 let pt = (x, y);
419 let val = !e.active_color;
420 if let Ok(o) = e.paint_point(pt, val) {
421 e.current_operation.extend(o);
422 }
423 });
424 }
425 }
426 // end of operation
427 Event::MouseButtonUp { .. } => self.modify(|e| {
428 dbg!(&e.current_operation.len());
429 let op = e
430 .current_operation
431 .drain(..)
432 .filter(|v| !v.old_val == v.val)
433 .collect::<Vec<_>>();
434 e.undo_stack.push(op);
435 dbg!(&e.undo_stack);
436 }),
437 Event::Quit { .. } => {
438 break 'running;
439 }
440 _ => {
441 self.modify(|_| ());
442 }
443 }
444 }
445 }
446 }
447}
diff --git a/src/consts.rs b/src/consts.rs
new file mode 100644
index 0000000..b5cea43
--- /dev/null
+++ b/src/consts.rs
@@ -0,0 +1,6 @@
1use sdl2::pixels::Color;
2
3pub const GRID_COLOR: Color = Color::RGB(64, 64, 64);
4pub const WHITE: Color = Color::RGB(255, 255, 255);
5pub const BLACK: Color = Color::RGB(0, 0, 0);
6
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..bf89508
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,10 @@
1mod app;
2mod consts;
3mod undo;
4
5use app::AppState;
6
7pub fn main() {
8 let sdl_context = sdl2::init().unwrap();
9 AppState::init(100, 100, &sdl_context).run();
10}
diff --git a/src/undo.rs b/src/undo.rs
new file mode 100644
index 0000000..2249fe7
--- /dev/null
+++ b/src/undo.rs
@@ -0,0 +1,133 @@
1#[derive(Copy, Clone, Debug)]
2pub struct ModifyRecord {
3 pub point: (i32, i32),
4 pub old_val: bool,
5 pub val: bool,
6}
7
8impl ModifyRecord {
9 pub fn new(point: (i32, i32), old_val: bool, val: bool) -> Self {
10 ModifyRecord {
11 point,
12 old_val,
13 val,
14 }
15 }
16}
17
18pub enum OpKind {
19 Undo,
20 Redo,
21}
22
23pub type Operation = Vec<ModifyRecord>;
24
25#[derive(Debug)]
26pub struct UndoStack<T> {
27 operations: Vec<T>,
28 position: Option<u32>,
29}
30
31impl<T> UndoStack<T>
32where
33 T: Clone,
34{
35 pub fn new() -> Self {
36 Self {
37 operations: Vec::with_capacity(64),
38 position: None,
39 }
40 }
41
42 pub fn push(&mut self, op: T) {
43 if let Some(p) = self.position {
44 // remove all operations past the newly pushed operation
45 for _ in 1 + (p as usize)..self.operations.len() {
46 self.operations.pop();
47 }
48 // advance position
49 self.position = Some(p + 1);
50 // add new operation
51 self.operations.push(op);
52 } else {
53 // empty ops list or undone till start of stack
54 // remove all operations past the newly pushed operation
55 self.operations.clear();
56 // advance position
57 self.position = Some(0);
58 // add new operation
59 self.operations.push(op);
60 }
61 }
62
63 pub fn undo(&mut self) -> Option<T> {
64 if let Some(p) = self.position {
65 self.position = p.checked_sub(1);
66 // we want to return a clone and not a reference because push deletes the item
67 return Some(self.operations[p as usize].clone());
68 }
69 return None;
70 }
71
72 pub fn redo(&mut self) -> Option<T> {
73 if let Some(p) = self.position {
74 if p < self.operations.len() as u32 - 1 {
75 self.position = Some(p + 1);
76 return Some(self.operations[1 + p as usize].clone());
77 }
78 } else if !self.operations.is_empty() {
79 self.position = Some(0);
80 return Some(self.operations[0].clone());
81 }
82 return None;
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 fn setup() -> UndoStack<u32> {
91 let mut stack = UndoStack::new();
92 stack.push(10);
93 stack.push(5);
94 stack.push(2);
95 stack
96 }
97
98 #[test]
99 fn undo_works() {
100 let mut stack = setup();
101 assert_eq!(stack.undo(), Some(2));
102 assert_eq!(stack.undo(), Some(5));
103 assert_eq!(stack.undo(), Some(10));
104 }
105 #[test]
106 fn redo_works() {
107 let mut stack = setup();
108 stack.undo();
109 stack.undo();
110 stack.undo();
111 assert_eq!(stack.redo(), Some(10));
112 assert_eq!(stack.redo(), Some(5));
113 assert_eq!(stack.redo(), Some(2));
114 }
115
116 #[test]
117 fn undo_push_redo() {
118 let mut stack = setup();
119 stack.undo();
120 stack.push(16);
121 assert_eq!(stack.redo(), None);
122 assert_eq!(stack.undo(), Some(16));
123 }
124
125 #[test]
126 fn stack_identity() {
127 let mut stack = setup();
128 stack.undo();
129 stack.redo();
130 stack.undo();
131 assert_eq!(stack.operations, setup().operations);
132 }
133}