TMSA Installation and Build Troubleshooting Report
1. Executive Summary
On a Xubuntu 26.04 LTS system, the Time Matters / TMSA application was installed from source at:
/home/christian/Homemade/tmsa
The application was successfully launched from source using a project-local Python virtual environment:
cd /home/christian/Homemade/tmsa . .venv/bin/activate python -X faulthandler -m src.tmsa
The GUI opened successfully under X11 and exited cleanly with code 0.
The Linux installer was then built successfully after two targeted fixes to setup.py. The final build produced both a Makeself installer and a distribution zip:
build/exe.linux-x86_64-3.10/TimeMatters-0.7.1-linux-x86_64.sh dist/TimeMatters-0.7.1-linux-x86_64.zip
The generated installer was tested in a clean disposable directory:
/home/christian/Homemade/tmsa-installer-test
The installer extracted successfully, created the run-tmsa -> ./tmsa symlink, and the packaged application launched successfully from:
/home/christian/Homemade/tmsa-installer-test/exe.linux-x86_64-3.10
The packaged application exited cleanly with code 0.
The main technical issue was not a missing dependency or broken system Python. The primary blocker was a Linux build-script ordering problem in setup.py: the script attempted to locate the cx_Freeze build output directory before cx_Freeze had created it. A second related failure occurred because another Linux packaging block still referenced a global build_dir value that remained None after the first fix. Both were resolved by resolving the Linux build directory lazily after build_exe completed.
A separate early environment issue occurred when an install command was run under sh=/=dash, where source is not available. That caused the first dependency installation attempt to target the pyenv base Python instead of the project virtual environment. The project .venv was recreated, activated correctly, and dependencies were installed cleanly.
2. Environment and Affected Components
| Item | Value |
|---|---|
| Operating system | Ubuntu/Xubuntu 26.04 LTS, codename resolute |
| Kernel | 7.0.0-27-generic |
| Shell | /bin/bash |
| System Python | Python 3.14.4 |
| System pip | pip 25.1.1 from /usr/lib/python3/dist-packages/pip |
| Git | 2.53.0 |
| pyenv | 2.6.8 |
| pyenv root | /home/christian/.pyenv |
| Selected project Python | Python 3.10.18 |
| Project venv | /home/christian/Homemade/tmsa/.venv |
| Project path | /home/christian/Homemade/tmsa |
| Repository commit | 0cf3637 |
| TMSA version built | 0.7.1 |
| GUI toolkit | Tkinter / Tk 8.6 |
| Installer tool | Makeself 2.5.0 |
| Build tool | cx_Freeze 7.0.0 |
| Build output directory | build/exe.linux-x86_64-3.10 |
| Runtime data directory | ~/.tmsa |
3. Symptoms
3.1. Initial environment risk
The system default Python was too new for the TMSA Linux build path:
/usr/bin/python3 Python 3.14.4 /usr/bin/pip3 pip 25.1.1 from /usr/lib/python3/dist-packages/pip (python 3.14)
The project README's Linux workflow expects Python 3.10 through pyenv, so using the system Python would have been risky and was avoided.
3.2. Virtual environment activation failure
During the first dependency installation attempt, activation failed:
install-requirements.sh: 17: source: not found
The subsequent which python and which pip output showed that the shell was using the pyenv base Python, not the project virtual environment:
/home/christian/.pyenv/shims/python Python 3.10.18 /home/christian/.pyenv/shims/pip pip 26.1.2 from /home/christian/.pyenv/versions/3.10.18/lib/python3.10/site-packages/pip (python 3.10)
3.3. First installer build failure
A clean python setup.py build_installer failed immediately:
Traceback (most recent call last):
File "/home/christian/Homemade/tmsa/setup.py", line 27, in <module>
raise RuntimeError(
RuntimeError: Could not find the cx_Freeze build output directory
No installer could be built from a clean tree with the original script.
3.4. Second installer build failure
After the first build-directory fix, the Makeself installer was generated, but the script still exited with an error:
Traceback (most recent call last):
File "/home/christian/Homemade/tmsa/setup.py", line 307, in <module>
if os.path.exists(os.path.join(build_dir, installer_name)):
File "/home/christian/.pyenv/versions/3.10.18/lib/python3.10/posixpath.py", line 76, in join
a = os.fspath(a)
TypeError: expected str, bytes or os.PathLike object, not NoneType
This showed that another Linux packaging block still expected the global build_dir variable to be populated.
3.5. Installer launch path confusion
The generated Makeself installer extracted successfully, but the first launch attempt failed:
./installer-test.sh: line 29: ./run-tmsa: No such file or directory
The installer had created a subdirectory named exe.linux-x86_64-3.10, so run-tmsa existed inside that directory rather than in the caller's current directory.
3.6. Non-blocking runtime warnings and UI observation
The packaged app emitted a Fontconfig warning on launch:
Fontconfig warning: "/usr/share/fontconfig/conf.avail/05-reset-dirs-sample.conf", line 6: unknown element "reset-dirs"
The application still launched and exited with code 0, so this was treated as non-blocking.
A screenshot of the source-run GUI showed that button text was slightly cramped. This was captured as a non-blocking UI rendering/layout note rather than an installation blocker.
4. Root Cause Analysis
4.1. Technical root cause: build directory resolved too early
The original setup.py attempted to locate the Linux cx_Freeze output directory at import time:
case 'linux':
base = None
import glob
build_dirs = glob.glob('build/exe.linux-*')
build_dir = build_dirs[0] if build_dirs else None
if build_dir is None:
raise RuntimeError(
'Could not find the cx_Freeze build output directory'
)
This is too early for a clean build. At the moment setup.py is imported, build/exe.linux-* does not yet exist. The directory is created later by:
self.run_command('build_exe')
Because the script required the directory before creating it, the clean build failed before build_exe could run.
4.2. Secondary technical root cause: stale global builddir assumption
After moving build-directory detection out of the import-time Linux block, the later post-setup Linux packaging code still assumed that a global build_dir variable was available:
if os.path.exists(os.path.join(build_dir, installer_name)):
Because build_dir remained None at that point, os.path.join() raised a TypeError. The script had to resolve the build directory again inside the post-setup Linux packaging block.
4.3. Contributing factor: shell mismatch during dependency installation
The first dependency installation attempt was run in a way that used sh=/=dash rather than bash. Ubuntu's dash shell does not support source, so virtual environment activation failed.
The portable POSIX-compatible activation form is:
. .venv/bin/activate
Using that form ensured dependencies were installed into:
/home/christian/Homemade/tmsa/.venv
rather than into the pyenv base environment.
4.4. Contributing factor: misleading postinstall message context
The installer's postinstall.sh says:
Unpacked successfully. Use ./run-tmsa to launch.
However, Makeself extracted into:
exe.linux-x86_64-3.10
Therefore, the correct launch sequence from the parent test directory was:
cd exe.linux-x86_64-3.10 ./run-tmsa
This is a usability note, not a runtime failure. The default extraction directory was intentionally left unchanged.
4.5. 5 Whys
Why did
python setup.py build_installerfail on a clean tree?Because
setup.pyraisedRuntimeError: Could not find the cx_Freeze build output directorybefore the build began.Why was the build output directory missing?
Because
build/exe.linux-*is created by thecx_Freezebuild_execommand, andbuild_exehad not run yet.Why did the script look for the directory before
build_exeran?Because Linux
build_dirdetection was performed at module import time in the top-level platform switch.Why did the script still fail after the first patch?
Because a later Linux packaging block still referenced the global
build_dirvariable, which was now intentionally left unset until afterbuild_exe.Why was this not caught by dependency checks?
Because the dependencies were valid.
pip checkreported no broken requirements. The failure was build-script control flow, not an absent Python package or missing system library.
5. Resolution and Recovery
5.1. Step 1: Use pyenv Python 3.10.18 locally
The project was pinned to the already-installed pyenv Python 3.10.18:
cd /home/christian/Homemade/tmsa export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH" eval "$(pyenv init -)" pyenv local 3.10.18
Validation:
/home/christian/.pyenv/shims/python Python 3.10.18 /home/christian/.pyenv/shims/pip pip 26.1.2 from /home/christian/.pyenv/versions/3.10.18/lib/python3.10/site-packages/pip (python 3.10)
Tkinter support was confirmed:
Python: 3.10.18 (main, Jul 6 2026, 08:46:01) [GCC 15.2.0] tkinter OK: 8.6
5.2. Step 2: Install Linux build dependencies
The following packages were confirmed installed, with cmake installed during the session:
makeself tcl-dev tk-dev build-essential python3-dev python3-pip cmake
5.3. Step 3: Recreate and activate the project virtual environment
The bad initial environment state was corrected by recreating the project virtual environment and activating it with POSIX-compatible syntax:
cd /home/christian/Homemade/tmsa rm -rf .venv python -m venv .venv . .venv/bin/activate
Validation:
/home/christian/Homemade/tmsa/.venv/bin/python Python 3.10.18 /home/christian/Homemade/tmsa/.venv/bin/pip pip 23.0.1 from /home/christian/Homemade/tmsa/.venv/lib/python3.10/site-packages/pip (python 3.10) sys.executable: /home/christian/Homemade/tmsa/.venv/bin/python sys.prefix: /home/christian/Homemade/tmsa/.venv sys.base_prefix: /home/christian/.pyenv/versions/3.10.18 venv active: True
5.4. Step 4: Install pinned Python requirements into .venv
Because cx_Freeze 7.0.0 requires older packaging tools, compatible versions were installed:
python -m pip install --upgrade pip python -m pip install "setuptools>=62.6,<70" "wheel>=0.42.0,<=0.43.0" python -m pip install -r etc/requirements-linux.txt python -m pip check
Validation:
Successfully installed ... cx_Freeze-7.0.0 ... tkextrafont-0.6.1 ... timezonefinder-6.5.0 ... No broken requirements found.
5.5. Step 5: Confirm runtime assets
The required runtime asset directories were present:
src/dll src/ephe
Important files included:
src/dll/libswe.so src/dll/swephexp.h src/dll/sweodef.h src/ephe/sefstars.txt src/ephe/seasnam.txt src/ephe/seorbel.txt
5.6. Step 6: Validate source run
The application was launched from source:
cd /home/christian/Homemade/tmsa . .venv/bin/activate python -X faulthandler -m src.tmsa
Validation:
DISPLAY=:0.0 WAYLAND_DISPLAY= /home/christian/Homemade/tmsa/.venv/bin/python Python 3.10.18 exit code: 0
The run created or used runtime configuration under ~/.tmsa:
/home/christian/.tmsa/logs/error.txt /home/christian/.tmsa/options/colors.json /home/christian/.tmsa/options/Cosmobiology.opt /home/christian/.tmsa/options/data_entry.json /home/christian/.tmsa/options/Ingress_Default.opt /home/christian/.tmsa/options/locations.json /home/christian/.tmsa/options/Natal_Default.opt /home/christian/.tmsa/options/Progressed_Default.opt /home/christian/.tmsa/options/recent.json /home/christian/.tmsa/options/Return_Default.opt /home/christian/.tmsa/options/Student_Natal.opt /home/christian/.tmsa/program_options.opt
The error log showed no recent error content.
5.7. Step 7: Patch setup.py for lazy Linux build-directory resolution
A helper was added to resolve the newest Linux cx_Freeze build directory after build_exe runs:
def resolve_linux_build_dir():
import glob
build_dirs = sorted(
glob.glob(os.path.join('build', 'exe.linux-*')),
key=os.path.getmtime,
reverse=True,
)
if not build_dirs:
raise RuntimeError('Could not find the cx_Freeze build output directory')
return build_dirs[0]
The import-time Linux block was simplified so it no longer failed before build_exe:
case 'linux':
base = None
The BuildInstaller.run() method was updated to resolve build_dir after build_exe:
self.run_command('build_exe')
build_dir = resolve_linux_build_dir()
The BuildAppImage.run() method was similarly updated. AppImage output was not tested in this session.
The post-setup Linux packaging block was also updated to resolve build_dir locally before using it:
elif sys.platform == 'linux':
os.makedirs(out_dir, exist_ok=True)
build_dir = resolve_linux_build_dir()
installer_name = f'TimeMatters-{VERSION}-linux-x86_64.sh'
The installer move was made safe for reruns:
if os.path.exists(installer_name):
shutil.move(installer_name, build_dir)
5.8. Step 8: Build the Linux installer
The installer build was rerun:
cd /home/christian/Homemade/tmsa . .venv/bin/activate rm -f TimeMatters-0.7.1-linux-x86_64.sh rm -f dist/TimeMatters-0.7.1-linux-x86_64.zip python setup.py build_installer
Validation:
=== build result === exit code: 0 build log: /tmp/tmsa-build-installer-after-direct-patch-20260706-224830.log
Generated files:
build/exe.linux-x86_64-3.10/TimeMatters-0.7.1-linux-x86_64.sh dist/TimeMatters-0.7.1-linux-x86_64.zip
The installer was identified as:
POSIX shell script executable (binary data), self-executable archive, Makeself 2.5.0
The installer and zip were both approximately 187M.
5.9. Step 9: Validate the generated installer
A clean installer test directory was created:
TEST_DIR="/home/christian/Homemade/tmsa-installer-test" rm -rf "$TEST_DIR" mkdir -p "$TEST_DIR" cd "$TEST_DIR" /home/christian/Homemade/tmsa/build/exe.linux-x86_64-3.10/TimeMatters-0.7.1-linux-x86_64.sh
Installer output:
Creating directory exe.linux-x86_64-3.10 Verifying archive integrity... 100% MD5 checksums are OK. All good. Uncompressing Time Matters 0.7.1 Installer 100% Unpacked successfully. Use ./run-tmsa to launch.
The extracted directory contained:
exe.linux-x86_64-3.10/tmsa exe.linux-x86_64-3.10/run-tmsa exe.linux-x86_64-3.10/postinstall.sh exe.linux-x86_64-3.10/dll/libswe.so
The launch symlink was correct:
run-tmsa -> ./tmsa
5.10. Step 10: Validate the packaged application
The packaged app was launched from the extracted directory:
cd /home/christian/Homemade/tmsa-installer-test/exe.linux-x86_64-3.10 ./run-tmsa
Validation:
PWD=/home/christian/Homemade/tmsa-installer-test/exe.linux-x86_64-3.10 DISPLAY=:0.0 Fontconfig warning: "/usr/share/fontconfig/conf.avail/05-reset-dirs-sample.conf", line 6: unknown element "reset-dirs" exit code: 0
The warning did not prevent the app from running.
6. Current Known-Good Configuration
6.1. Source checkout
/home/christian/Homemade/tmsa
6.2. Repository state
Repository commit: 0cf3637 Untracked local files: .python-version, .venv/ Modified file: setup.py Backup created: setup.py.before-build-dir-fix
6.3. Python environment
pyenv Python: 3.10.18 Project venv: /home/christian/Homemade/tmsa/.venv Active Python: /home/christian/Homemade/tmsa/.venv/bin/python Tk version: 8.6
6.4. Dependency sanity
python -m pip check No broken requirements found.
6.5. Source launch command
cd /home/christian/Homemade/tmsa . .venv/bin/activate python -X faulthandler -m src.tmsa
6.6. Build command
cd /home/christian/Homemade/tmsa . .venv/bin/activate python setup.py build_installer
6.7. Installer artifacts
build/exe.linux-x86_64-3.10/TimeMatters-0.7.1-linux-x86_64.sh dist/TimeMatters-0.7.1-linux-x86_64.zip
6.8. Packaged app launch command
cd /home/christian/Homemade/tmsa-installer-test/exe.linux-x86_64-3.10 ./run-tmsa
7. Action Items
7.1. Completed
[X]Confirmed system OS, kernel, shell, Python, pip, git, and pyenv.[X]Cloned TMSA into/home/christian/Homemade/tmsa.[X]Recorded repository commit0cf3637.[X]Confirmed pyenv Python3.10.18exists.[X]Set repo-local.python-versionto3.10.18.[X]Installed or confirmed Linux build dependencies:makeself,tcl-dev,tk-dev,build-essential,python3-dev,python3-pip, andcmake.[X]Confirmed Tkinter works with pyenv Python 3.10.18.[X]Recreated project.venvafter initial shell activation issue.[X]Installed all pinned Linux Python requirements into.venv.[X]Confirmedpython -m pip checkreports no broken requirements.[X]Confirmedsrc/dllandsrc/epheassets exist.[X]Launched TMSA successfully from source.[X]Patchedsetup.pyto resolve Linuxbuild_dirafterbuild_exe.[X]Patched post-setup Linux packaging block to resolvebuild_dirlocally.[X]Built the Makeself installer successfully.[X]Builtdist/TimeMatters-0.7.1-linux-x86_64.zipsuccessfully.[X]Ran the generated installer in a clean test directory.[X]Launched the packaged app successfully via./run-tmsafrom the extracted directory.
7.2. Recommended follow-up
[ ]Keepsetup.py.before-build-dir-fixuntil the local patch is no longer needed.[ ]Consider committing the localsetup.pypatch to a branch if this installation will be rebuilt later.[ ]Consider opening an upstream issue or pull request for the Linux build-directory resolution fix.[ ]Add.venv/to local/global ignore rules if needed; do not commit the virtual environment.[ ]Decide whether to keep the disposable installer test directory or replace it with a permanent packaged-app directory.[ ]Re-check the non-blocking cramped button text on different DPI/font settings before changing application layout code.[ ]Monitor whether the Fontconfig warning appears elsewhere; treat it as low priority unless it correlates with font rendering problems.[ ]If a desktop launcher is desired later, create one that points to the chosen packagedrun-tmsapath or to the source-run command.
8. Quick Reference
8.1. Activate the project environment
cd /home/christian/Homemade/tmsa export PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH" eval "$(pyenv init -)" pyenv local 3.10.18 . .venv/bin/activate
8.2. Verify the environment
which python
python --version
python -m pip check
python - <<'PY'
import tkinter
import tkextrafont
import cx_Freeze
import numpy
import timezonefinder
print("tkinter OK:", tkinter.TkVersion)
print("tkextrafont OK")
print("cx_Freeze OK:", cx_Freeze.__version__)
print("numpy OK:", numpy.__version__)
print("timezonefinder OK")
PY
8.3. Run from source
cd /home/christian/Homemade/tmsa . .venv/bin/activate python -X faulthandler -m src.tmsa
8.4. Build installer
cd /home/christian/Homemade/tmsa . .venv/bin/activate python setup.py build_installer
8.5. Inspect generated artifacts
ls -lh build/exe.linux-x86_64-3.10/TimeMatters-0.7.1-linux-x86_64.sh ls -lh dist/TimeMatters-0.7.1-linux-x86_64.zip unzip -l dist/TimeMatters-0.7.1-linux-x86_64.zip
8.6. Test installer in a disposable directory
TEST_DIR="/home/christian/Homemade/tmsa-installer-test" rm -rf "$TEST_DIR" mkdir -p "$TEST_DIR" cd "$TEST_DIR" /home/christian/Homemade/tmsa/build/exe.linux-x86_64-3.10/TimeMatters-0.7.1-linux-x86_64.sh cd exe.linux-x86_64-3.10 ./run-tmsa
8.7. Good state
Source app launches and exits with code 0. Installer builds and exits with code 0. Installer extracts successfully. Packaged app launches from exe.linux-x86_64-3.10 and exits with code 0.
8.8. Bad states observed
source: not found RuntimeError: Could not find the cx_Freeze build output directory TypeError: expected str, bytes or os.PathLike object, not NoneType ./run-tmsa: No such file or directory from parent extraction directory
9. Notes and Caveats
- The source checkout remains at
/home/christian/Homemade/tmsa. - The successful packaged-app test used the sibling disposable directory
/home/christian/Homemade/tmsa-installer-test. - The default Makeself extraction directory
exe.linux-x86_64-3.10was left unchanged by preference. - The installer's
postinstall.shcreatesrun-tmsa -> ./tmsainside the extracted directory. - The postinstall message is technically correct only after changing into the extraction directory.
- The
Fontconfig warningobserved during packaged launch did not prevent a successful launch or clean exit. - Button text appeared slightly cramped in the GUI screenshot. This was not treated as an install failure.
- The display hardware is a Panasonic Toughbook CF-31-5; the cramped button text may be related to limited screen real estate, display resolution, DPI scaling, or Tk font/layout assumptions rather than a packaging fault.
- The
BuildAppImagepath was patched in the same style asBuildInstallerbut was not tested in this session. - The local
setup.pypatch is not present in upstream commit0cf3637unless later committed or submitted upstream.