Skip to content

Commit be7afbf

Browse files
committed
Appimage refactor - step 1
.agents/codebase-insights.txt: appimage-scripts/build_appimage2.sh: nix/packages/default.nix: Signed-off-by: Tzanko Matev <[email protected]>
1 parent c4e4746 commit be7afbf

File tree

3 files changed

+311
-2
lines changed

3 files changed

+311
-2
lines changed

.agents/codebase-insights.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
- `DapParser` now buffers data via `add_bytes` and exposes parsed payloads with
1111
`get_message()`, so callers need to drain messages until `None` after feeding
1212
new bytes.
13-
14-
1513
- The db-backend DAP server now responds to the `configurationDone` request.
1614

1715
- Electron lifecycle: the backend-manager process is spawned in
@@ -25,3 +23,4 @@
2523
- Interpreter overrides (e.g. `CODETRACER_PYTHON_INTERPRETER`) are now treated as authoritative; if the configured path cannot be resolved we error instead of silently falling back to PATH.
2624
- `ct record` verifies that `codetracer_python_recorder` is importable before launching the db backend and prints actionable guidance if the module is missing or broken.
2725
- Sudoku test-program datasets include intentionally invalid boards (e.g., examples #3 and #6) with duplicate digits inside a sub-grid; solvers should detect and report these gracefully.
26+
- `appimage-scripts/build_appimage.sh` assumes a devshell: it invokes `nix build`/`nix eval` directly and runs `npm install`/`npx yarn`, so it needs host network access and the git worktree metadata that a pure derivation build lacks.
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
#!/usr/bin/env bash
2+
3+
# THIS SCRIPT IS TO BE RUN IN OUR DEV SHELL
4+
5+
# The goal of this script is to prepare an `AppDir` (see the spec below)
6+
# and then to launch the appimagetool to create an AppImage.
7+
#
8+
# AppDir spec: https://github.com/AppImage/AppImageSpec/blob/master/draft.md#appdir
9+
# appimagetool: https://github.com/AppImage/appimagetool
10+
11+
set -euo pipefail
12+
13+
cleanup() {
14+
echo "Performing cleanup..."
15+
rm -rf ./squashfs-root
16+
}
17+
18+
trap cleanup EXIT
19+
20+
ROOT_PATH=$(git rev-parse --show-toplevel)
21+
export ROOT_PATH
22+
23+
APP_DIR="${ROOT_PATH}/squashfs-root"
24+
export APP_DIR
25+
26+
if [ -e "${ROOT_PATH}/CodeTracer.AppImage" ]; then
27+
rm -rf "${ROOT_PATH}/CodeTracer.AppImage"
28+
fi
29+
30+
rm -rf "${APP_DIR}"
31+
mkdir -p "${APP_DIR}/"{bin,src,lib,views}
32+
33+
# This environment variable controls where build artifacts and static resources end up.
34+
export NIX_CODETRACER_EXE_DIR="${APP_DIR}"
35+
36+
CURRENT_NIX_SYSTEM=$(nix eval --impure --raw --expr 'builtins.currentSystem')
37+
APPIMAGE_DEPS=$(nix build "${ROOT_PATH}#packages.${CURRENT_NIX_SYSTEM}.appimageDeps" --no-link --print-out-paths | tail -n1)
38+
39+
copy_lib_from_derivation() {
40+
local lib_name=$1
41+
local copied=false
42+
43+
if [ -f "${APPIMAGE_DEPS}/lib/${lib_name}" ]; then
44+
cp -L "${APPIMAGE_DEPS}/lib/${lib_name}" "${APP_DIR}/lib/"
45+
copied=true
46+
fi
47+
48+
if [ -f "${APPIMAGE_DEPS}/lib64/${lib_name}" ]; then
49+
cp -L "${APPIMAGE_DEPS}/lib64/${lib_name}" "${APP_DIR}/lib/"
50+
copied=true
51+
fi
52+
53+
if [ "${copied}" = false ]; then
54+
return 1
55+
fi
56+
57+
return 0
58+
}
59+
60+
copy_required_lib() {
61+
local lib_name=$1
62+
if ! copy_lib_from_derivation "${lib_name}"; then
63+
echo "Required library ${lib_name} is missing from ${APPIMAGE_DEPS}" >&2
64+
exit 1
65+
fi
66+
}
67+
68+
copy_optional_lib() {
69+
local lib_name=$1
70+
if ! copy_lib_from_derivation "${lib_name}"; then
71+
echo "Optional library ${lib_name} not found; continuing." >&2
72+
fi
73+
}
74+
75+
copy_binary_from_derivation() {
76+
local binary_name=$1
77+
local destination="${APP_DIR}/bin/${binary_name}"
78+
79+
if [ ! -f "${APPIMAGE_DEPS}/bin/${binary_name}" ]; then
80+
echo "Required binary ${binary_name} is missing from ${APPIMAGE_DEPS}" >&2
81+
exit 1
82+
fi
83+
84+
cp -L "${APPIMAGE_DEPS}/bin/${binary_name}" "${destination}"
85+
}
86+
87+
# Copy shared libraries that were previously fetched via individual nix build/eval calls.
88+
copy_required_lib "libsqlite3.so.0"
89+
copy_optional_lib "libsqlite3.so"
90+
copy_required_lib "libpcre.so.1"
91+
copy_required_lib "libzip.so.5"
92+
copy_required_lib "libssl.so.3"
93+
copy_optional_lib "libssl.so"
94+
copy_required_lib "libcrypto.so.3"
95+
copy_optional_lib "libcrypto.so"
96+
copy_required_lib "libuv.so.1"
97+
98+
# Copy binaries sourced from Nix packages.
99+
copy_binary_from_derivation "wazero"
100+
copy_binary_from_derivation "cargo-stylus"
101+
copy_binary_from_derivation "nargo"
102+
copy_binary_from_derivation "ctags"
103+
copy_binary_from_derivation "curl"
104+
copy_binary_from_derivation "node"
105+
106+
# Install Ruby
107+
bash "${ROOT_PATH}/appimage-scripts/install_ruby.sh"
108+
109+
cat <<'EOF' >"${APP_DIR}/bin/ruby"
110+
#!/usr/bin/env bash
111+
112+
HERE="${HERE:-..}"
113+
114+
# TODO: This includes references to x86_64. What about aarch64?
115+
export RUBYLIB="${HERE}/ruby/lib/ruby/3.3.0:${HERE}/ruby/lib/ruby/3.3.0/x86_64-linux:${RUBYLIB}"
116+
117+
"${HERE}/ruby/bin/ruby" "$@"
118+
119+
EOF
120+
121+
# ruby recorder
122+
cp -Lr "${ROOT_PATH}/libs/codetracer-ruby-recorder" "${APP_DIR}/"
123+
124+
# Copy over electron
125+
# bash "${ROOT_PATH}/appimage-scripts/install_electron_nix.sh"
126+
bash "${ROOT_PATH}/appimage-scripts/install_electron.sh"
127+
128+
# Setup node deps
129+
bash "${ROOT_PATH}/appimage-scripts/setup_node_deps.sh"
130+
131+
# Build our css files
132+
bash "${ROOT_PATH}/appimage-scripts/build_css.sh"
133+
134+
# Build/setup nim-based files
135+
bash "${ROOT_PATH}/appimage-scripts/build_with_nim.sh"
136+
137+
cat <<'EOF' >"${APP_DIR}/bin/ct"
138+
#!/usr/bin/env bash
139+
140+
HERE=${HERE:-$(dirname "$(readlink -f "${0}")")}
141+
142+
# TODO: This includes references to x86_64. What about aarch64?
143+
144+
exec "${HERE}/bin/ct_unwrapped" "$@"
145+
146+
EOF
147+
148+
# Build/setup db-backend
149+
bash "${ROOT_PATH}/appimage-scripts/build_db_backend.sh"
150+
151+
# Build/setup backend-manager
152+
bash "${ROOT_PATH}/appimage-scripts/build_backend_manager.sh"
153+
154+
# Ensure copied binaries are executable.
155+
chmod +x "${APP_DIR}/bin/"{cargo-stylus,ctags,curl,nargo,node,wazero}
156+
157+
# Collect transitive shared library dependencies from Nix store.
158+
# shellcheck disable=SC2046
159+
cp -n $(lddtree -l "${APP_DIR}/bin/ctags" | grep -v glibc | grep /nix) "${APP_DIR}/lib"
160+
161+
# shellcheck disable=SC2046
162+
cp -n $(lddtree -l "${APP_DIR}/bin/curl" | grep -v glibc | grep /nix) "${APP_DIR}/lib"
163+
164+
# shellcheck disable=SC2046
165+
cp -n $(lddtree -l "${APP_DIR}/bin/node" | grep -v glibc | grep /nix) "${APP_DIR}/lib"
166+
167+
# shellcheck disable=SC2046
168+
cp -n $(lddtree -l "${APP_DIR}/bin/cargo-stylus" | grep -v glibc | grep /nix) "${APP_DIR}/lib"
169+
170+
chmod -R +x "${APP_DIR}/bin"
171+
chmod -R +x "${APP_DIR}/electron"
172+
173+
chmod -R 777 "${APP_DIR}"
174+
175+
cp "${ROOT_PATH}/src/helpers.js" "${APP_DIR}/src/helpers.js"
176+
cp "${ROOT_PATH}/src/helpers.js" "${APP_DIR}/helpers.js"
177+
178+
cp "${ROOT_PATH}/src/frontend/index.html" "${APP_DIR}/src/index.html"
179+
cp "${ROOT_PATH}/src/frontend/index.html" "${APP_DIR}/index.html"
180+
181+
cp "${ROOT_PATH}/src/frontend/subwindow.html" "${APP_DIR}/subwindow.html"
182+
cp "${ROOT_PATH}/src/frontend/subwindow.html" "${APP_DIR}/src/subwindow.html"
183+
184+
cp "${ROOT_PATH}/views/server_index.ejs" "${APP_DIR}/views/server_index.ejs"
185+
186+
rm -rf "${APP_DIR}/config"
187+
rm -rf "${APP_DIR}/public"
188+
cp -Lr "${ROOT_PATH}/src/config" "${APP_DIR}/config"
189+
190+
# Enable the installation prompt
191+
sed -i "s/skipInstall.*/skipInstall: false/g" "${APP_DIR}/config/default_config.yaml"
192+
193+
cp -Lr "${ROOT_PATH}/src/public" "${APP_DIR}/public"
194+
chmod -R +wr "${APP_DIR}/public"
195+
196+
cp -Lr "${ROOT_PATH}/src/public/dist/frontend_bundle.js" "${APP_DIR}"
197+
198+
# Create AppRun script
199+
cat <<'EOF' >"${APP_DIR}/AppRun"
200+
#!/usr/bin/env bash
201+
202+
export HERE=$(dirname "$(readlink -f "${0}")")
203+
204+
# TODO: This includes references to x86_64. What about aarch64?
205+
export LINKS_PATH_DIR=$HERE
206+
export PATH="${HERE}/bin:${PATH}"
207+
export CODETRACER_RUBY_RECORDER_PATH="${HERE}/codetracer-ruby-recorder/gems/codetracer-pure-ruby-recorder/bin/codetracer-pure-ruby-recorder"
208+
209+
exec ${HERE}/bin/ct "$@"
210+
EOF
211+
212+
chmod +x "${APP_DIR}/AppRun"
213+
214+
# Copy over desktop file
215+
cp "${ROOT_PATH}/resources/codetracer.desktop" "${APP_DIR}/"
216+
217+
# Copy over resources
218+
cp -Lr "${ROOT_PATH}/resources" "${APP_DIR}"
219+
220+
SRC_ICONSET_DIR="${ROOT_PATH}/resources/Icon.iconset"
221+
222+
# TODO: discover these dynamically perhaps
223+
for SIZE in 16 32 128 256 512; do
224+
XSIZE="${SIZE}x${SIZE}"
225+
DST_PATH="${APP_DIR}/usr/share/icons/hicolor/${XSIZE}/apps/"
226+
DOUBLE_SIZE_DST_PATH="${APP_DIR}/usr/share/icons/hicolor/${XSIZE}@2/apps/"
227+
mkdir -p "${DST_PATH}" "${DOUBLE_SIZE_DST_PATH}"
228+
cp "${SRC_ICONSET_DIR}/icon_${XSIZE}.png" "${DST_PATH}/codetracer.png"
229+
cp "${SRC_ICONSET_DIR}/icon_${XSIZE}@2x.png" "${DOUBLE_SIZE_DST_PATH}/codetracer.png"
230+
done
231+
232+
cp "${ROOT_PATH}/resources/Icon.iconset/icon_256x256.png" "${APP_DIR}/codetracer.png"
233+
234+
CURRENT_ARCH=$(uname -m)
235+
if [[ "${CURRENT_ARCH}" == "aarch64" ]]; then
236+
INTERPRETER_PATH=/lib/ld-linux-aarch64.so.1
237+
else
238+
INTERPRETER_PATH=/lib64/ld-linux-x86-64.so.2
239+
fi
240+
241+
# Patchelf the executable's interpreter
242+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/ct_unwrapped"
243+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/db-backend"
244+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/db-backend-record"
245+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/backend-manager"
246+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/nargo"
247+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/wazero"
248+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/ctags"
249+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/curl"
250+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/cargo-stylus"
251+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/bin/node"
252+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${APP_DIR}/ruby/bin/ruby"
253+
254+
# Clear up the executable's rpath
255+
patchelf --remove-rpath "${APP_DIR}/bin/ct_unwrapped"
256+
patchelf --remove-rpath "${APP_DIR}/bin/db-backend"
257+
patchelf --remove-rpath "${APP_DIR}/bin/db-backend-record"
258+
patchelf --remove-rpath "${APP_DIR}/bin/backend-manager"
259+
patchelf --remove-rpath "${APP_DIR}/bin/nargo"
260+
patchelf --remove-rpath "${APP_DIR}/bin/wazero"
261+
patchelf --remove-rpath "${APP_DIR}/bin/ctags"
262+
patchelf --remove-rpath "${APP_DIR}/bin/curl"
263+
patchelf --remove-rpath "${APP_DIR}/bin/cargo-stylus"
264+
patchelf --remove-rpath "${APP_DIR}/bin/node"
265+
patchelf --remove-rpath "${APP_DIR}/ruby/bin/ruby"
266+
267+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/node"
268+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/ct_unwrapped"
269+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/db-backend"
270+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/db-backend-record"
271+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/backend-manager"
272+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/nargo"
273+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/wazero"
274+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/ctags"
275+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/curl"
276+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/bin/node"
277+
patchelf --set-rpath "\$ORIGIN/../lib" "${APP_DIR}/ruby/bin/ruby"
278+
279+
APPIMAGE_ARCH=${CURRENT_ARCH}
280+
if [[ "${APPIMAGE_ARCH}" == "aarch64" ]]; then
281+
# The appimagetool has its own convention for specifying the ARM64 arch.
282+
APPIMAGE_ARCH=arm_aarch64
283+
fi
284+
285+
# Use AppImage tool to create AppImage itself
286+
ARCH=${APPIMAGE_ARCH} appimagetool "${APP_DIR}" CodeTracer.AppImage
287+
288+
patchelf --set-interpreter "${INTERPRETER_PATH}" "${ROOT_PATH}/CodeTracer.AppImage"
289+
patchelf --remove-rpath "${ROOT_PATH}/CodeTracer.AppImage"
290+
291+
echo "============================"
292+
echo "AppImage successfully built!"
293+
echo "============================"

nix/packages/default.nix

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,23 @@
127127
'';
128128
};
129129

130+
appimageDeps = pkgs.symlinkJoin {
131+
name = "codetracer-appimage-deps";
132+
paths = [
133+
sqlite
134+
pcre
135+
libzip
136+
openssl
137+
libuv
138+
cargo-stylus
139+
wazero
140+
noir
141+
pkgs.universal-ctags
142+
pkgs.curl
143+
pkgs.nodejs_20
144+
];
145+
};
146+
130147
indexJavascript = stdenv.mkDerivation {
131148
name = "index.js";
132149
pname = "index.js";

0 commit comments

Comments
 (0)