Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
265 lines (225 loc) · 10.2 KB
/
Copy pathMakefile
File metadata and controls
265 lines (225 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# SPDX-License-Identifier: GPL-3.0-only
#
# LinuxChat build and install Makefile.
#
# This Makefile is responsible for:
# - compiling the C GTK frontend
# - preparing a per-user Python virtual environment for provider scripts
# - installing the binary, wrapper launcher, resources, and source files
# - installing desktop integration files and icons
# - removing installed artifacts during uninstall
#
# Design notes:
# - The compiled ELF binary is installed as linuxchat-cgtk-bin.
# - A small shell wrapper named $(APP) is installed into $(BINDIR) so the
# application always launches from the installed application directory.
# - Python provider scripts are not bundled into a wheel or package here;
# instead, they are copied into the installed application directory and a
# per-user virtual environment is created under ~/.config/LinuxChat/env.
#
# Risk notes:
# - install and env-setup intentionally manipulate user-owned state under
# ~/.config/LinuxChat, even when invoked through sudo.
# - The TARGET_USER / TARGET_HOME / TARGET_GROUP logic exists to avoid
# accidentally creating root-owned configuration and virtualenv files for
# a normal desktop user.
# - Any change to install paths or wrapper behavior affects runtime file
# discovery for resources and provider scripts.
# Public application launcher name installed into $(BINDIR).
APP=linuxchat-cgtk
# C compiler used for all native compilation units.
CC=gcc
# Compiler flags:
# - optimize reasonably for release usage
# - enable common warnings
# - build as C11
# - pull include flags from pkg-config for all required libraries
CFLAGS=-O2 -pipe -Wall -Wextra -std=c11 $(shell pkg-config --cflags gtk4 gio-2.0 glib-2.0 gobject-2.0 pango sqlite3)
# Linker flags from pkg-config for GTK, GLib, Pango, SQLite, and friends.
LDFLAGS=$(shell pkg-config --libs gtk4 gio-2.0 glib-2.0 gobject-2.0 pango sqlite3)
# All C source files in src/.
SRC=$(wildcard src/*.c)
# Matching object file list generated from SRC.
OBJ=$(SRC:.c=.o)
# Python provider/helper scripts in src/.
PY_SCRIPTS=$(wildcard src/*.py)
# All top-level source files in src/ except object files.
#
# These files are copied into the installed application directory so the
# runtime can access Python provider launchers and related source assets.
SRC_FILES=$(shell find src -maxdepth 1 -type f ! -name '*.o' | sort)
# Installation prefix. The caller may override PREFIX at invocation time.
PREFIX?=/usr/local
# Directory where user-facing launchers are installed.
BINDIR=$(PREFIX)/bin
# Application runtime directory.
#
# In this project the installed runtime files are placed directly under
# $(BINDIR), so APPDIR matches BINDIR.
APPDIR=$(BINDIR)
# Desktop entry installation path.
DESKTOPDIR=/usr/share/applications
# SVG/scalable icon path. Kept for future compatibility even if only PNG
# is currently installed below.
ICONDIR=/usr/share/icons/hicolor/scalable/apps
# PNG icon installation path.
ICONPNGDIR=/usr/share/icons/hicolor/256x256/apps
# Effective desktop target user.
#
# Behavior:
# - If sudo is in use, prefer SUDO_USER so configuration is created for the
# invoking desktop user instead of root.
# - Otherwise fall back to the current login user.
TARGET_USER?=$(shell if [ -n "$$SUDO_USER" ]; then printf '%s' "$$SUDO_USER"; else id -un; fi)
# Effective home directory for the target user.
#
# This is used to place LinuxChat runtime configuration and Python virtualenv
# state in the correct user-owned location.
TARGET_HOME?=$(shell getent passwd "$(TARGET_USER)" | cut -d: -f6)
# Effective primary group for the target user.
TARGET_GROUP?=$(shell id -gn "$(TARGET_USER)" 2>/dev/null || printf %s "$(TARGET_USER)")
# LinuxChat runtime configuration directory in the target user's home.
CONFIGDIR=$(TARGET_HOME)/.config/LinuxChat
# Python virtual environment directory used by runtime provider scripts.
PYENV_DIR=$(CONFIGDIR)/env
# Python interpreter inside the managed virtual environment.
PYENV_PY=$(PYENV_DIR)/bin/python
# Python packages required by the provider adapters.
PY_PACKAGES=openai anthropic
# Default target:
# - prepare runtime Python environment
# - build the native GTK binary
all: env-setup $(APP)
# Macro: RUN_AS_TARGET_USER
#
# Role:
# Execute one shell command as the target desktop user when the Makefile is
# being run as root via sudo, otherwise execute it as the current user.
#
# Why this exists:
# - Creating a virtualenv under ~/.config/LinuxChat as root would leave the
# environment unusable or awkward for the real desktop user.
# - This helper keeps runtime-managed files user-owned.
define RUN_AS_TARGET_USER
if [ "$$(id -u)" -eq 0 ] && [ -n "$(TARGET_USER)" ] && [ "$(TARGET_USER)" != "root" ]; then \
su -s /bin/sh "$(TARGET_USER)" -c '$(1)'; \
else \
sh -lc '$(1)'; \
fi
endef
# Target: env-setup
#
# Role:
# Prepare LinuxChat runtime configuration and the Python provider virtual
# environment before build or install.
#
# Behavior:
# - create ~/.config/LinuxChat
# - create ~/.config/LinuxChat/.config
# - seed config.json from config.default.json if it does not already exist
# - fix ownership when running under sudo
# - create the Python virtual environment if missing
# - upgrade pip
# - install/upgrade required provider packages
#
# Risk notes:
# - This target mutates user-owned state outside the build tree.
# - If config ownership is wrong, Python provider setup can fail later.
env-setup:
@if [ "$$(id -u)" -eq 0 ] && [ -n "$(TARGET_USER)" ] && [ "$(TARGET_USER)" != "root" ]; then install -d "$(CONFIGDIR)"; install -d "$(CONFIGDIR)/.config"; if [ ! -f "$(CONFIGDIR)/.config/config.json" ]; then install -m 644 config.default.json "$(CONFIGDIR)/.config/config.json"; fi; chown -R "$(TARGET_USER):$(TARGET_GROUP)" "$(CONFIGDIR)"; else install -d "$(CONFIGDIR)"; install -d "$(CONFIGDIR)/.config"; if [ ! -f "$(CONFIGDIR)/.config/config.json" ]; then install -m 644 config.default.json "$(CONFIGDIR)/.config/config.json"; fi; fi
$(call RUN_AS_TARGET_USER,if [ ! -x "$(PYENV_PY)" ]; then python3 -m venv "$(PYENV_DIR)"; fi)
$(call RUN_AS_TARGET_USER,"$(PYENV_PY)" -m pip install --upgrade pip)
$(call RUN_AS_TARGET_USER,"$(PYENV_PY)" -m pip install --upgrade $(PY_PACKAGES))
# Link the final native application binary from all compiled object files.
$(APP): $(OBJ)
$(CC) $(OBJ) -o $@ $(LDFLAGS)
# Pattern rule for compiling one C translation unit into one object file.
#
# Explicit header dependencies are listed so edits to shared headers force
# recompilation of all relevant objects.
src/%.o: src/%.c src/app.h src/commands.h src/storage.h src/attachments.h src/settings_db.h src/api_manager.h
$(CC) $(CFLAGS) -c $< -o $@
# Target: install
#
# Role:
# Install the LinuxChat runtime into the chosen prefix.
#
# Behavior:
# - ensures env-setup has completed
# - installs the compiled ELF binary as linuxchat-cgtk-bin
# - generates a tiny wrapper launcher named $(APP)
# - copies resources and src files into the install tree
# - fixes directory/file permissions
# - makes provider Python files executable
# - optionally chowns runtime directories back to the target user
# - installs desktop integration files and PNG icon
#
# Design notes:
# - The wrapper script changes into APPDIR before exec so relative resource
# paths used by the runtime continue to work after installation.
install: env-setup $(APP)
install -d $(DESTDIR)$(BINDIR)
install -d $(DESTDIR)$(APPDIR)/resources/icons
install -d $(DESTDIR)$(APPDIR)/src
install -m 755 $(APP) $(DESTDIR)$(APPDIR)/linuxchat-cgtk-bin
printf '%s\n' '#!/bin/sh' 'APPDIR="$(BINDIR)"' 'cd "$$APPDIR" || exit 1' 'exec "$$APPDIR/linuxchat-cgtk-bin" "$$@"' > $(DESTDIR)$(BINDIR)/$(APP)
chmod 755 $(DESTDIR)$(BINDIR)/$(APP)
install -m 644 resources/app.css $(DESTDIR)$(APPDIR)/resources/app.css
@if ls resources/linuxchat-spinner.png >/dev/null 2>&1; then install -m 644 resources/linuxchat-spinner.png $(DESTDIR)$(APPDIR)/resources/linuxchat-spinner.png; fi
@if ls resources/icons/*.png >/dev/null 2>&1; then install -m 644 resources/icons/*.png $(DESTDIR)$(APPDIR)/resources/icons/; fi
install -m 644 $(SRC_FILES) $(DESTDIR)$(APPDIR)/src/
find $(DESTDIR)$(APPDIR)/resources -type d -exec chmod 755 {} \;
find $(DESTDIR)$(APPDIR)/src -type d -exec chmod 755 {} \;
find $(DESTDIR)$(APPDIR)/resources -type f -exec chmod 644 {} \;
find $(DESTDIR)$(APPDIR)/src -type f -exec chmod 644 {} \;
find $(DESTDIR)$(APPDIR)/src -type f -name '*.py' -exec chmod 755 {} \;
@if [ "$$(id -u)" -eq 0 ] && [ -n "$(TARGET_USER)" ] && [ "$(TARGET_USER)" != "root" ]; then \
chown -R "$(TARGET_USER):$(TARGET_GROUP)" "$(DESTDIR)$(APPDIR)/resources" "$(DESTDIR)$(APPDIR)/src"; \
fi
install -d $(DESTDIR)$(DESKTOPDIR)
install -m 644 packaging/io.github.linuxchat.cgtk.desktop $(DESTDIR)$(DESKTOPDIR)/io.github.linuxchat.cgtk.desktop
install -d $(DESTDIR)$(ICONDIR)
install -d $(DESTDIR)$(ICONPNGDIR)
@if ls resources/icons/io.github.linuxchat.cgtk.png >/dev/null 2>&1; then install -m 644 resources/icons/io.github.linuxchat.cgtk.png $(DESTDIR)$(ICONPNGDIR)/io.github.linuxchat.cgtk.png; fi
# Target: uninstall
#
# Role:
# Remove LinuxChat files previously installed by this Makefile.
#
# Behavior:
# - removes the wrapper launcher
# - removes copied resources and source runtime files
# - removes the compiled ELF binary
# - removes desktop integration files
# - removes the installed PNG icon
#
# Note:
# - This target does not remove ~/.config/LinuxChat user data or the Python
# virtual environment under the user's config directory.
uninstall:
rm -f $(DESTDIR)$(BINDIR)/$(APP)
rm -rf $(DESTDIR)$(APPDIR)/src $(DESTDIR)$(APPDIR)/resources
rm -f $(DESTDIR)$(APPDIR)/linuxchat-cgtk-bin
rm -f $(DESTDIR)$(DESKTOPDIR)/io.github.linuxchat.cgtk.desktop
rm -f $(DESTDIR)$(ICONPNGDIR)/io.github.linuxchat.cgtk.png
# Target: clean
#
# Role:
# Remove build artifacts generated inside the source tree.
#
# Behavior:
# - deletes object files
# - deletes the compiled launcher binary
# - deletes Python bytecode cache directories in src/
clean:
rm -f $(OBJ) $(APP)
rm -rf src/__pycache__
# Target: run
#
# Role:
# Convenience developer target for building and running the application from
# the current build tree without installation.
run: $(APP)
./$(APP)
# Mark these targets as phony so make does not confuse them with real files.
.PHONY: all env-setup install uninstall clean run