This site hosts my projects.
changeset: 7995:7f208da5f70c
tag: tip
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Tue Jul 08 10:29:58 2025 +0200
summary: Ignore build artifacts.
diff -r 63ba50e3d1f4 -r 7f208da5f70c .hgignore
--- a/.hgignore Tue Jul 08 10:10:08 2025 +0200
+++ b/.hgignore Tue Jul 08 10:29:58 2025 +0200
@@ -1,5 +1,8 @@
syntax: glob
*.bak
+Dockerfile*.ci.files
+timeline-*-Win64Setup.exe
+timeline-*.zip
mikado.png
mikado.pdf
changeset: 7994:63ba50e3d1f4
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Tue Jul 08 10:10:08 2025 +0200
summary: Run tests in Windows environment as well.
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Sun Jul 06 09:10:31 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Tue Jul 08 10:10:08 2025 +0200
@@ -5,6 +5,7 @@
ENV PYTHON_DIR=C:\\Python
ENV PYTHON_PIP_EXE=C:\\Python\\Scripts\\pip.exe
ENV INNO_DIR=C:\\Inno
+ENV GETTEXT_DIR=C:\\gettext
RUN dnf update -y
RUN dnf install -y wine
@@ -16,6 +17,7 @@
RUN wget https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
+RUN wget https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.25-v1.17/gettext0.25-iconv1.17-static-64.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
@@ -27,6 +29,7 @@
&& wine $PYTHON_PIP_EXE install --no-warn-script-location markdown \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location icalendar \
&& wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$INNO_DIR \
+ && wine gettext0.25-iconv1.17-static-64.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$GETTEXT_DIR \
&& wineserver -w'
CMD ["xvfb-run", "python", "tools/build-windows-exe-from-source-zip.py", "--output-list", "Dockerfile.zip-to-exe.ci.files"]
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/dialogs/preferences/view.py
--- a/source/timelinelib/wxgui/dialogs/preferences/view.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/dialogs/preferences/view.py Tue Jul 08 10:10:08 2025 +0200
@@ -468,7 +468,7 @@
"date_time_text": _("Date && Time"),
"week_start_text": _("Week start on:"),
"week_start_choices": [_("Monday"), _("Sunday")],
- "workday_length_text": f'{_("Workday Length in hours")}:',
+ "workday_length_text": _("Workday Length in hours:"),
"fonts_text": _("Fonts"),
"colours_text": _("Colours"),
"major_strip_text": _("Major Strips:"),
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/frames/mainframe/lockhandler.py
--- a/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Tue Jul 08 10:10:08 2025 +0200
@@ -110,7 +110,7 @@
return f"{self._path}.lock"
def _report_other_process_uses_lockfile(self, lockpath):
- message = _(f"""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
+ message = _("""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
This lockfile must be removed in order be able to continue editing the timeline!
- The lockfile is found at: {lockpath}""")
+ The lockfile is found at: {lockpath}""").format(lockpath=lockpath)
display_warning_message(message, self._main_frame)
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Sun Jul 06 09:10:31 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Tue Jul 08 10:10:08 2025 +0200
@@ -51,6 +51,11 @@
if len(zips) != 1:
sys.exit("ERROR: Could not find source zip.")
archive = timelinetools.packaging.zipfile.ZipFile(".", zips[0]).extract_to(tempdir)
+ subprocess.check_call([
+ "wine",
+ f"{environ['PYTHON_DIR']}\\python.exe",
+ archive.get_path("tools/execute-specs.py"),
+ ])
archive.change_constant(
"tools/winbuildtools/inno/timeline.iss",
"AppVerName",
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 translations/timeline.pot
--- a/translations/timeline.pot Sun Jul 06 09:10:31 2025 +0200
+++ b/translations/timeline.pot Tue Jul 08 10:10:08 2025 +0200
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-04-06 23:09+0200\n"
+"POT-Creation-Date: 2025-07-08 07:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -3076,6 +3076,10 @@
msgid "Sunday"
msgstr ""
+#: source\timelinelib\wxgui\dialogs\preferences\view.py:471
+msgid "Workday Length in hours:"
+msgstr ""
+
#: source\timelinelib\wxgui\dialogs\preferences\view.py:472
msgid "Fonts"
msgstr ""
changeset: 7994:63ba50e3d1f4
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Tue Jul 08 10:10:08 2025 +0200
summary: Run tests in Windows environment as well.
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Sun Jul 06 09:10:31 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Tue Jul 08 10:10:08 2025 +0200
@@ -5,6 +5,7 @@
ENV PYTHON_DIR=C:\\Python
ENV PYTHON_PIP_EXE=C:\\Python\\Scripts\\pip.exe
ENV INNO_DIR=C:\\Inno
+ENV GETTEXT_DIR=C:\\gettext
RUN dnf update -y
RUN dnf install -y wine
@@ -16,6 +17,7 @@
RUN wget https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
+RUN wget https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.25-v1.17/gettext0.25-iconv1.17-static-64.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
@@ -27,6 +29,7 @@
&& wine $PYTHON_PIP_EXE install --no-warn-script-location markdown \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location icalendar \
&& wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$INNO_DIR \
+ && wine gettext0.25-iconv1.17-static-64.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$GETTEXT_DIR \
&& wineserver -w'
CMD ["xvfb-run", "python", "tools/build-windows-exe-from-source-zip.py", "--output-list", "Dockerfile.zip-to-exe.ci.files"]
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/dialogs/preferences/view.py
--- a/source/timelinelib/wxgui/dialogs/preferences/view.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/dialogs/preferences/view.py Tue Jul 08 10:10:08 2025 +0200
@@ -468,7 +468,7 @@
"date_time_text": _("Date && Time"),
"week_start_text": _("Week start on:"),
"week_start_choices": [_("Monday"), _("Sunday")],
- "workday_length_text": f'{_("Workday Length in hours")}:',
+ "workday_length_text": _("Workday Length in hours:"),
"fonts_text": _("Fonts"),
"colours_text": _("Colours"),
"major_strip_text": _("Major Strips:"),
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 source/timelinelib/wxgui/frames/mainframe/lockhandler.py
--- a/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Sun Jul 06 09:10:31 2025 +0200
+++ b/source/timelinelib/wxgui/frames/mainframe/lockhandler.py Tue Jul 08 10:10:08 2025 +0200
@@ -110,7 +110,7 @@
return f"{self._path}.lock"
def _report_other_process_uses_lockfile(self, lockpath):
- message = _(f"""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
+ message = _("""The lockfile used to protect the timeline from concurrent updates is opened by another program or process.
This lockfile must be removed in order be able to continue editing the timeline!
- The lockfile is found at: {lockpath}""")
+ The lockfile is found at: {lockpath}""").format(lockpath=lockpath)
display_warning_message(message, self._main_frame)
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Sun Jul 06 09:10:31 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Tue Jul 08 10:10:08 2025 +0200
@@ -51,6 +51,11 @@
if len(zips) != 1:
sys.exit("ERROR: Could not find source zip.")
archive = timelinetools.packaging.zipfile.ZipFile(".", zips[0]).extract_to(tempdir)
+ subprocess.check_call([
+ "wine",
+ f"{environ['PYTHON_DIR']}\\python.exe",
+ archive.get_path("tools/execute-specs.py"),
+ ])
archive.change_constant(
"tools/winbuildtools/inno/timeline.iss",
"AppVerName",
diff -r dd4c4b92daa3 -r 63ba50e3d1f4 translations/timeline.pot
--- a/translations/timeline.pot Sun Jul 06 09:10:31 2025 +0200
+++ b/translations/timeline.pot Tue Jul 08 10:10:08 2025 +0200
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2025-04-06 23:09+0200\n"
+"POT-Creation-Date: 2025-07-08 07:50+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -3076,6 +3076,10 @@
msgid "Sunday"
msgstr ""
+#: source\timelinelib\wxgui\dialogs\preferences\view.py:471
+msgid "Workday Length in hours:"
+msgstr ""
+
#: source\timelinelib\wxgui\dialogs\preferences\view.py:472
msgid "Fonts"
msgstr ""
commit e222ec25134d69f05300d7e04bab67f24ba9a3f2
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Mon Jul 7 09:07:00 2025 +0200
Newsletter june 2025.
diff --git a/bib/simplepeter.md b/bib/simplepeter.md
new file mode 100644
index 0000000..fd51a1f
--- /dev/null
+++ b/bib/simplepeter.md
@@ -0,0 +1,6 @@
+---
+date: 2025-07-07 08:27:00
+title: Why Can't We Make Simple Software? - Peter van Hardenberg
+type: youtube
+link: https://youtu.be/czzAVuVz7u4
+---
diff --git a/posts/2025/07/07/newsletter-june/post.md b/posts/2025/07/07/newsletter-june/post.md
new file mode 100644
index 0000000..b4e771e
--- /dev/null
+++ b/posts/2025/07/07/newsletter-june/post.md
@@ -0,0 +1,19 @@
+---
+date: 2025-07-07 08:15:48
+title: Newsletter June 2025: Ink & Switch
+tags: newsletter
+---
+
+This month I watched the presentation [](bib:simplepeter). This introduced me
+to [Ink & Switch](https://www.inkandswitch.com):
+
+> An independent research lab exploring the future of tools for thought.
+>
+> We envision a new computer that amplifies human intelligence.
+
+This caught my interest because they mention [Doug
+Engelbart](https://en.wikipedia.org/wiki/Douglas_Engelbart) which I learned
+about from [Alan
+Kay](https://archive.rickardlindberg.me/writing/alan-kay-notes/). I have been
+inspired by Kay's work and perhaps Ink & Switch is doing work in the same vein
+that is worth keeping and eye on.
changeset: 7992:7bdcd3d4dc9a
parent: 7974:d18436c111ab
user: roger <roger@rolidata.se>
date: Sun Jul 06 09:06:44 2025 +0200
summary: Fixed problem with LANG setting for translations
diff -r d18436c111ab -r 7bdcd3d4dc9a source/timeline.py
--- a/source/timeline.py Sat Apr 19 13:41:56 2025 +0200
+++ b/source/timeline.py Sun Jul 06 09:06:44 2025 +0200
@@ -50,7 +50,7 @@
# The appropriate environment variables are set on other systems
import wx
loc = wx.Locale()
- language = loc.GetLanguageName(loc.GetSystemLanguage())
+ language = loc.GetLanguageCanonicalName(loc.GetSystemLanguage())
os.environ['LANG'] = language
if getattr(sys, 'frozen', False):
os.chdir(os.path.dirname(sys.executable))
changeset: 7993:dd4c4b92daa3
parent: 7992:7bdcd3d4dc9a
parent: 7991:ca9470a3fae4
user: roger <roger@rolidata.se>
date: Sun Jul 06 09:10:31 2025 +0200
summary: Merged
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 Dockerfile.zip-to-exe.ci
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Dockerfile.zip-to-exe.ci Sun Jul 06 09:10:31 2025 +0200
@@ -0,0 +1,32 @@
+FROM fedora:41
+
+ENV WINEDEBUG=-all
+ENV WINEPREFIX=/opt/wine
+ENV PYTHON_DIR=C:\\Python
+ENV PYTHON_PIP_EXE=C:\\Python\\Scripts\\pip.exe
+ENV INNO_DIR=C:\\Inno
+
+RUN dnf update -y
+RUN dnf install -y wine
+RUN dnf install -y wget
+RUN dnf install -y xorg-x11-server-Xvfb
+
+ENV PYTHON_VERSION=3.13.5
+ENV WXPYTHON_VERSION=4.2.3
+
+RUN wget https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-amd64.exe
+RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
+
+# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
+
+RUN umask 0 && xvfb-run bash -c '\
+ wine python-$PYTHON_VERSION-amd64.exe /quiet TargetDir=$PYTHON_DIR \
+ && wine $PYTHON_PIP_EXE install --no-warn-script-location pyinstaller \
+ && wine $PYTHON_PIP_EXE install --no-warn-script-location wxpython==$WXPYTHON_VERSION \
+ && wine $PYTHON_PIP_EXE install --no-warn-script-location humblewx \
+ && wine $PYTHON_PIP_EXE install --no-warn-script-location markdown \
+ && wine $PYTHON_PIP_EXE install --no-warn-script-location icalendar \
+ && wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES /DIR=$INNO_DIR \
+ && wineserver -w'
+
+CMD ["xvfb-run", "python", "tools/build-windows-exe-from-source-zip.py", "--output-list", "Dockerfile.zip-to-exe.ci.files"]
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 Dockerfile.zip.ci
--- a/Dockerfile.zip.ci Sun Jul 06 09:06:44 2025 +0200
+++ b/Dockerfile.zip.ci Sun Jul 06 09:10:31 2025 +0200
@@ -7,4 +7,4 @@
RUN /venv/bin/pip install icalendar
RUN /venv/bin/pip install Markdown
-CMD ["xvfb-run", "/venv/bin/python", "tools/build-source-zip.py", "--output-list", "Dockerfile.linux311.ci.files", "--output-list", "Dockerfile.zip.ci.files"]
+CMD ["xvfb-run", "/venv/bin/python", "tools/build-source-zip.py", "--output-list", "Dockerfile.zip.ci.files"]
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 documentation/changelog.rst
--- a/documentation/changelog.rst Sun Jul 06 09:06:44 2025 +0200
+++ b/documentation/changelog.rst Sun Jul 06 09:10:31 2025 +0200
@@ -3,11 +3,11 @@
Version 2.11.0
--------------
- **Planned to be Released on 9 August 2025.**
-
- *Don't want to wait for the final release? Try the beta version!*
-
- * `Download source <https://projects.rickardlindberg.me/artifacts/timeline/>`_.
+**Planned to be Released on 9 August 2025.**
+
+*Don't want to wait for the final release? Try the beta version!*
+
+* Beta versions: |betas|_
New features, enhancements:
@@ -34,6 +34,10 @@
* ``Corrupted icon file causes exception``
Use a default icon when icon file is corrupted and disable error dialog.
+Windows installer:
+
+* The Windows installer is now built on Linux/Wine and only for 64 bit platforms.
+
Version 2.10.0
--------------
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 documentation/conf.py
--- a/documentation/conf.py Sun Jul 06 09:06:44 2025 +0200
+++ b/documentation/conf.py Sun Jul 06 09:10:31 2025 +0200
@@ -69,13 +69,24 @@
break
return latest_version
+def get_current_version():
+ with open("changelog.rst") as f:
+ while True:
+ line = f.readline()
+ if line.startswith("Version "):
+ return line[8:].strip()
+ return "null"
+
rst_epilog = """
.. |latest_zip| replace:: timeline-%(version)s.zip
.. _latest_zip: https://projects.rickardlindberg.me/artifacts/timeline/%(version)s/timeline-%(version)s.zip
.. |latest_exe| replace:: timeline-%(version)s-Win32Setup.exe
.. _latest_exe: https://projects.rickardlindberg.me/artifacts/timeline/%(version)s/timeline-%(version)s-Win32Setup.exe
+.. |betas| replace:: all beta versions
+.. _betas: https://projects.rickardlindberg.me/artifacts/timeline/%(current_version)s
""" % {
"version": get_latest_version(),
+ "current_version": get_current_version(),
"exe_version": get_latest_version().replace(".", ""),
}
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 documentation/installing.rst
--- a/documentation/installing.rst Sun Jul 06 09:06:44 2025 +0200
+++ b/documentation/installing.rst Sun Jul 06 09:10:31 2025 +0200
@@ -23,7 +23,7 @@
Download one of the following installers:
* Latest release: |latest_exe|_
-* Beta version: `latest Windows build <https://projects.rickardlindberg.me/artifacts/timeline/>`_
+* Beta versions: |betas|_
The beta version is for users that want to try the latest features and give
feedback on them before a release.
@@ -56,7 +56,7 @@
Download one of the following source packages:
* Latest release: |latest_zip|_
-* Beta version: `latest source build <https://projects.rickardlindberg.me/artifacts/timeline/>`_
+* Beta versions: |betas|_
The beta version is for users that want to try the latest features and give
feedback on them before a release.
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 source/timelinelib/meta/version.py
--- a/source/timelinelib/meta/version.py Sun Jul 06 09:06:44 2025 +0200
+++ b/source/timelinelib/meta/version.py Sun Jul 06 09:10:31 2025 +0200
@@ -30,7 +30,7 @@
if TYPE:
parts.append(TYPE)
if REVISION_HASH or REVISION_DATE:
- revision_parts = [item for item in (REVISION_HASH, REVISION_DATE) if item]
+ revision_parts = [item for item in (REVISION_DATE, REVISION_HASH) if item]
parts.append("(%s)" % " ".join(revision_parts))
return " ".join(parts)
@@ -38,7 +38,7 @@
def get_filename_version():
parts = ["timeline", get_version_number_string()]
if not is_final():
- parts.extend([TYPE, REVISION_HASH, REVISION_DATE])
+ parts.extend([TYPE, REVISION_DATE, REVISION_HASH])
return "-".join(parts)
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 test/unit/meta/version.py
--- a/test/unit/meta/version.py Sun Jul 06 09:06:44 2025 +0200
+++ b/test/unit/meta/version.py Sun Jul 06 09:10:31 2025 +0200
@@ -43,13 +43,13 @@
def test_full(self):
self.assertEqual(
version.get_full_version(),
- "1.2.3 beta (abc123 2015-11-11)"
+ "1.2.3 beta (2015-11-11 abc123)"
)
def test_filename(self):
self.assertEqual(
version.get_filename_version(),
- "timeline-1.2.3-beta-abc123-2015-11-11"
+ "timeline-1.2.3-beta-2015-11-11-abc123"
)
def test_version_numer_string(self):
@@ -77,13 +77,13 @@
def test_full(self):
self.assertEqual(
version.get_full_version(),
- "1.2.3 development (abc123 2015-11-11)"
+ "1.2.3 development (2015-11-11 abc123)"
)
def test_filename(self):
self.assertEqual(
version.get_filename_version(),
- "timeline-1.2.3-development-abc123-2015-11-11"
+ "timeline-1.2.3-development-2015-11-11-abc123"
)
def test_version_numer_string(self):
@@ -111,7 +111,7 @@
def test_full(self):
self.assertEqual(
version.get_full_version(),
- "1.2.3 (abc123 2015-11-11)"
+ "1.2.3 (2015-11-11 abc123)"
)
def test_filename(self):
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/build-source-zip.py
--- a/tools/build-source-zip.py Sun Jul 06 09:06:44 2025 +0200
+++ b/tools/build-source-zip.py Sun Jul 06 09:10:31 2025 +0200
@@ -22,7 +22,6 @@
import argparse
import json
import os
-import shutil
import tempfile
import timelinetools.packaging.repository
@@ -40,11 +39,8 @@
def package_source(arguments):
- tempdir = tempfile.mkdtemp()
- try:
+ with tempfile.TemporaryDirectory(dir=".") as tempdir:
create_source_zip(arguments, tempdir)
- finally:
- shutil.rmtree(tempdir)
def create_source_zip(arguments, tempdir):
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/build-windows-exe-from-source-zip.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/build-windows-exe-from-source-zip.py Sun Jul 06 09:10:31 2025 +0200
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
+#
+# This file is part of Timeline.
+#
+# Timeline is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Timeline is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
+
+
+from os import environ
+from os.path import basename, join
+import argparse
+import glob
+import json
+import subprocess
+import sys
+import tempfile
+
+import timelinetools.packaging.repository
+
+
+def main():
+ build_winows_exe(parse_arguments())
+
+
+def parse_arguments():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--revision", default="tip")
+ parser.add_argument("--output-list")
+ return parser.parse_args()
+
+
+def build_winows_exe(arguments):
+ with tempfile.TemporaryDirectory(dir=".") as tempdir:
+ build_winows_exe_tmp(arguments, tempdir)
+
+
+def build_winows_exe_tmp(arguments, tempdir):
+ zips = glob.glob("timeline-*.zip")
+ if len(zips) != 1:
+ sys.exit("ERROR: Could not find source zip.")
+ archive = timelinetools.packaging.zipfile.ZipFile(".", zips[0]).extract_to(tempdir)
+ archive.change_constant(
+ "tools/winbuildtools/inno/timeline.iss",
+ "AppVerName",
+ f"Timeline {archive.get_version_number_string()}"
+ )
+ exe_name = f"{archive.get_filename_version()}-Win64Setup"
+ archive.change_constant(
+ "tools/winbuildtools/inno/timeline.iss",
+ "OutputBaseFilename",
+ exe_name
+ )
+ archive.change_constant(
+ "source/timelinelib/config/paths.py",
+ "_ROOT",
+ '"."'
+ )
+ archive.clean_pyc_files()
+ subprocess.check_call([
+ "wine",
+ f"{environ['PYTHON_DIR']}\\Scripts\\pyinstaller.exe",
+ "timeline.spec",
+ ], cwd=archive.get_path("tools/winbuildtools"))
+ subprocess.check_call([
+ "wineserver",
+ "-w",
+ ])
+ subprocess.check_call([
+ "cp",
+ "-r",
+ archive.get_path("translations"),
+ archive.get_path("icons"),
+ archive.get_path("tools/winbuildtools/dist"),
+ ])
+ subprocess.check_call([
+ "wine",
+ f"{environ['INNO_DIR']}\\ISCC.exe",
+ "inno/timeline.iss",
+ ], cwd=archive.get_path("tools/winbuildtools"))
+ subprocess.check_call([
+ "wineserver",
+ "-w",
+ ])
+ subprocess.check_call([
+ "mv",
+ archive.get_path(f"tools/winbuildtools/inno/out/{exe_name}.exe"),
+ f"{exe_name}.exe",
+ ])
+ write_output_list(
+ arguments=arguments,
+ exe_file=join(".", f"{exe_name}.exe"),
+ version=archive.get_version_number_string(),
+ )
+
+
+def write_output_list(arguments, exe_file, version):
+ if arguments.output_list:
+ with open(arguments.output_list, "w") as f:
+ f.write(json.dumps({
+ "artifacts": [
+ {
+ "source": exe_file,
+ "destination": join(version, basename(exe_file)),
+ }
+ ],
+ }))
+
+
+if __name__ == '__main__':
+ main()
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/build_win32_installer.py
--- a/tools/build_win32_installer.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-Working directory = BUILD_DIR
-COPYDIR Copies from TIMELINE_DIR to BUILD_DIR
-"""
-
-import argparse
-import sys
-import os
-import shutil
-import tempfile
-import subprocess
-import timelinetools.packaging.repository
-
-
-TIMELINE_DIR = os.path.abspath("..\\..\\")
-BUILD_DIR = os.path.abspath(".\\target")
-ARCHIVE = "archive"
-ARTIFACT = "artifact"
-
-COPYFILE = 0
-COPYDIR = 1
-PUSHD = 3
-POPD = 4
-RUNCMD = 5
-RUNPYSCRIPT = 6
-ANNOTATE = 8
-RUNPYTEST = 9
-
-ACTION_NAMES = {COPYFILE: "COPYFILE",
- COPYDIR: "COPYDIR",
- PUSHD: "PUSHD",
- POPD: "POPD",
- RUNCMD: "RUNCMD",
- RUNPYSCRIPT: "RUNPYSCRIPT",
- RUNPYTEST: "RUNPYTEST"
- }
-
-
-known_targets = ("win32Installer")
-
-
-win32InstallerActions = (
- (ANNOTATE, "Run Tests", ""),
- (RUNPYTEST, ["tools", "execute-specs.py"], ""),
-
- (ANNOTATE, "Modify source files", ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_paths_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_version_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_factory_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_iss_win32.py"], ""),
-
- (ANNOTATE, "Create a target directory for the build", ""),
- (COPYDIR, ["source", "timelinelib"], ["builddir", "timelinelib"]),
- (COPYDIR, ["tools", "winbuildtools", "inno"], ["builddir", "inno"]),
- (COPYFILE, ["source", "timeline.py"], ["builddir", "timeline.py"]),
- (COPYFILE, ["tools", "winbuildtools", "setup.py"], ["builddir", "setup.py"]),
- (COPYFILE, ["COPYING"], ["builddir", "COPYING"]),
- (COPYFILE, ["tools", "winbuildtools", "inno", "WINSTALL"], ["builddir", "WINSTALL"]),
-
- (ANNOTATE, "Create distribution directory", ""),
- (COPYDIR, ["icons"], ["builddir", "icons"]),
- (RUNPYSCRIPT, ["builddir", "setup.py"], "py2exe"),
- (COPYDIR, ["translations"], ["builddir", "dist", "translations"]),
- (COPYDIR, ["icons"], ["builddir", "dist", "icons"]),
- (COPYDIR, ["tools"], ["builddir", "dist", "tools"]),
-
- (ANNOTATE, "Create installer executable", ""),
- (RUNCMD, "python", ["builddir", "dist", "tools", "generate-mo-files.py"]),
-
- (ANNOTATE, "Create Setup executable", ""),
- (RUNCMD, "iscc.exe", ["builddir", "inno", "timelineWin32.iss"]),
-
- (ANNOTATE, "Deliver executable artifact", ""),
- (COPYFILE, [ARTIFACT], [ARTIFACT]),
-
- (ANNOTATE, "Done", ""),
-)
-
-
-actions = {"win32Installer": win32InstallerActions}
-
-
-class Target():
-
- def __init__(self, target):
- print("-------------------------------------------------------")
- print(" %s" % ("Building target %s" % target))
- print("-------------------------------------------------------")
- self.target = target
- self.actions = actions[target]
- self.ACTION_METHODS = {COPYFILE: self.copyfile,
- COPYDIR: self.copydir,
- PUSHD: self.pushd,
- POPD: self.popd,
- RUNCMD: self.runcmd,
- RUNPYSCRIPT: self.runpyscript,
- RUNPYTEST: self.runpytest,
- ANNOTATE: self.annotate}
-
- def build(self, arguments, artifact_dir):
- temp_dir = tempfile.mkdtemp()
- try:
- self.assert_that_target_is_known()
- self.setup_and_create_directories(arguments, artifact_dir, temp_dir)
- self.execute_actions()
- finally:
- #shutil.rmtree(temp_dir)
- pass
-
- def assert_that_target_is_known(self):
- if self.target not in known_targets:
- print("The target %s is unknown" % self.target)
- print("BUILD FAILED")
- sys.exit(1)
-
- def setup_and_create_directories(self, arguments, artifact_dir, temp_dir):
- self.artifact_dir = artifact_dir
- self.project_dir = self.create_project_directory(arguments, temp_dir)
- print("Artifact dir: %s" % self.artifact_dir)
- print("Project dir: %s" % self.project_dir)
- print("Working dir: %s" % os.getcwd())
-
- def create_project_directory(self, arguments, temp_dir):
- print("Create project directory")
- repository = timelinetools.packaging.repository.Repository()
- self.archive = repository.archive(arguments.revision, temp_dir, ARCHIVE)
- return os.path.join(temp_dir, ARCHIVE)
-
- def execute_actions(self):
- count = 0
- total = len([actions for action in self.actions if action[0] is not ANNOTATE])
- try:
- for action, src, dst in self.actions:
- if action is not ANNOTATE:
- count += 1
- print("Action %2d(%2d): %s" % (count, total, ACTION_NAMES[action]))
- self.ACTION_METHODS[action](src, dst)
- print("BUILD DONE")
- except Exception as ex:
- print(str(ex))
- print("BUILD FAILED")
- sys.exit(1)
-
- def annotate(self, src, dst):
- self.print_header(src)
-
- def copyfile(self, src, dst):
- if src[0] == ARTIFACT:
- f = os.path.join(self.project_dir, self.get_artifact_src_name())
- t = os.path.join(self.artifact_dir, self.get_artifact_target_name())
- else:
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copyfile(f, t)
-
- def copydir(self, src, dst):
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copytree(f, t)
-
- def runpyscript(self, src, arg):
- try:
- script_path = os.path.join(self.project_dir, *src)
- self.print_src_dst(script_path, arg)
- if src[-1] == "setup.py":
- self.pushd(os.path.join(self.project_dir, "builddir"), None)
- success, msg = self.run_pyscript(script_path, [arg])
- self.popd(None, None)
- else:
- success, msg = self.run_pyscript(script_path, [self.project_dir, arg])
- if not success:
- raise Exception(msg)
- except Exception as ex:
- pass
-
- def runpytest(self, src, dst):
- script_path = os.path.join(self.project_dir, *src)
- self.pushd(os.path.dirname(script_path), None)
- self.print_src_dst(src, os.path.abspath(dst))
- success, msg = self.run_pyscript(script_path, [], display_stderr=True)
- if not success:
- print('Msg:', msg)
- if msg:
- raise Exception(msg)
- self.popd(None, None)
-
- def runcmd(self, src, dst):
- t = os.path.join(self.project_dir, *dst)
- self.pushd(os.path.dirname(t), None)
- self.print_src_dst(src, t)
- success, msg = self.run_command([src, t])
- self.popd(None, None)
- if not success:
- raise Exception(msg)
-
- def pushd(self, src, dst):
- self.print_src_dst(os.getcwd(), os.path.abspath(src))
- self.cwd = os.getcwd()
- os.chdir(src)
-
- def popd(self, src, dst):
- self.print_src_dst(None, self.cwd)
- print(" dst: %s" % self.cwd)
- os.chdir(self.cwd)
-
- def run_pyscript(self, script, args=[], display_stderr=False):
- return self.run_command(["python", script] + args, display_stderr)
-
- def run_command(self, cmd, display_stderr=False):
- if display_stderr:
- rc = subprocess.call(cmd)
- return rc == 0, ""
- else:
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out = p.communicate()
- print(out)
- if p.returncode == 0:
- return True, out[0]
- else:
- return False, out[1]
-
- def print_header(self, message):
- print("-------------------------------------------------------")
- print(" %s" % message)
- print("-------------------------------------------------------")
-
- def print_src_dst(self, src, dst):
- if src is not None:
- print(" src: %s" % src)
- if dst is not None:
- print(" dst: %s" % dst)
-
- def get_artifact_src_name(self):
- versionfile = os.path.join(self.project_dir, "source", "timelinelib", "meta", "version.py")
- f = open(versionfile, "r")
- text = f.read()
- lines = text.split("\n")
- for line in lines:
- if line[0:7] == "VERSION":
- break
- f.close()
- # VERSION = (0, 14, 0)
- line = line.split("(", 1)[1]
- line = line.split(")", 1)[0]
- major, minor, bug = line. split(", ")
- return "SetupTimeline%s%s%sPy2ExeWin32.exe" % (major, minor, bug)
-
- def get_artifact_target_name(self):
- return "%s-Win32Setup.exe" % self.archive.get_filename_version()
-
-
-def main():
- artifactdir = os.path.join(sys.path[0], "..")
- Target("win32Installer").build(parse_arguments(), artifactdir)
-
-
-def parse_arguments():
- parser = argparse.ArgumentParser()
- parser.add_argument("--revision", default="tip")
- return parser.parse_args()
-
-
-if __name__ == "__main__":
- main()
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/build_win32_installer_py27.py
--- a/tools/build_win32_installer_py27.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-Working directory = BUILD_DIR
-COPYDIR Copies from TIMELINE_DIR to BUILD_DIR
-"""
-
-import argparse
-import sys
-import os
-import shutil
-import tempfile
-import subprocess
-import timelinetools.packaging.repository
-
-
-TIMELINE_DIR = os.path.abspath("..\\..\\")
-BUILD_DIR = os.path.abspath(".\\target")
-ARCHIVE = "archive"
-ARTIFACT = "artifact"
-
-COPYFILE = 0
-COPYDIR = 1
-PUSHD = 3
-POPD = 4
-RUNCMD = 5
-RUNPYSCRIPT = 6
-ANNOTATE = 8
-RUNPYTEST = 9
-
-ACTION_NAMES = {COPYFILE: "COPYFILE",
- COPYDIR: "COPYDIR",
- PUSHD: "PUSHD",
- POPD: "POPD",
- RUNCMD: "RUNCMD",
- RUNPYSCRIPT: "RUNPYSCRIPT",
- RUNPYTEST: "RUNPYTEST"
- }
-
-
-known_targets = ("win32Installer")
-
-
-win32InstallerActions = (
- (ANNOTATE, "Run Tests", ""),
- (RUNPYTEST, ["tools", "execute-specs.py"], ""),
-
- (ANNOTATE, "Modify source files", ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_paths_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_version_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_factory_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_iss_win32_py27.py"], ""),
-
- (ANNOTATE, "Create a target directory for the build", ""),
- (COPYDIR, ["source", "timelinelib"], ["builddir", "timelinelib"]),
- (COPYDIR, ["dependencies", "timelinelib", "icalendar-3.2\icalendar"], ["builddir", "icalendar"]),
- (COPYDIR, ["dependencies", "timelinelib", "pytz-2012j\pytz"], ["builddir", "pytz"]),
- (COPYDIR, ["dependencies", "timelinelib", "markdown-2.0.3", "markdown"], ["builddir", "markdown"]),
- (COPYDIR, ["tools", "winbuildtools", "inno"], ["builddir", "inno"]),
- (COPYFILE, ["source", "timeline.py"], ["builddir", "timeline.py"]),
- (COPYFILE, ["tools", "winbuildtools", "setup.py"], ["builddir", "setup.py"]),
- (COPYFILE, ["COPYING"], ["builddir", "COPYING"]),
- (COPYFILE, ["tools", "winbuildtools", "inno", "WINSTALL"], ["builddir", "WINSTALL"]),
-
- (ANNOTATE, "Create distribution directory", ""),
- (COPYDIR, ["icons"], ["builddir", "icons"]),
- (RUNPYSCRIPT, ["builddir", "setup.py"], "py2exe"),
- (COPYDIR, ["translations"], ["builddir", "dist", "translations"]),
- (COPYDIR, ["icons"], ["builddir", "dist", "icons"]),
- (COPYDIR, ["tools"], ["builddir", "dist", "tools"]),
-
- (ANNOTATE, "Create installer executable", ""),
- (RUNCMD, "python", ["builddir", "dist", "tools", "generate-mo-files.py"]),
-
- (ANNOTATE, "Create Setup executable", ""),
- (RUNCMD, "iscc.exe", ["builddir", "inno", "timelineWin32Py27.iss"]),
-
- (ANNOTATE, "Deliver executable artifact", ""),
- (COPYFILE, [ARTIFACT], [ARTIFACT]),
-
- (ANNOTATE, "Done", ""),
- )
-
-
-actions = {"win32Installer": win32InstallerActions}
-
-
-class Target():
-
- def __init__(self, target):
- print("-------------------------------------------------------")
- print(" %s" % ("Building target %s" % target))
- print("-------------------------------------------------------")
- self.target = target
- self.actions = actions[target]
- self.ACTION_METHODS = {COPYFILE: self.copyfile,
- COPYDIR: self.copydir,
- PUSHD: self.pushd,
- POPD: self.popd,
- RUNCMD: self.runcmd,
- RUNPYSCRIPT: self.runpyscript,
- RUNPYTEST: self.runpytest,
- ANNOTATE: self.annotate}
-
- def build(self, arguments, artifact_dir):
- temp_dir = tempfile.mkdtemp()
- try:
- self.assert_that_target_is_known()
- self.setup_and_create_directories(arguments, artifact_dir, temp_dir)
- self.execute_actions()
- finally:
- # shutil.rmtree(temp_dir)
- pass
-
- def assert_that_target_is_known(self):
- if self.target not in known_targets:
- print("The target %s is unknown" % self.target)
- print("BUILD FAILED")
- sys.exit(1)
-
- def setup_and_create_directories(self, arguments, artifact_dir, temp_dir):
- self.artifact_dir = artifact_dir
- self.project_dir = self.create_project_directory(arguments, temp_dir)
- print("Artifact dir: %s" % self.artifact_dir)
- print("Project dir: %s" % self.project_dir)
- print("Working dir: %s" % os.getcwd())
-
- def create_project_directory(self, arguments, temp_dir):
- print("Create project directory")
- repository = timelinetools.packaging.repository.Repository()
- self.archive = repository.archive(arguments.revision, temp_dir, ARCHIVE)
- return os.path.join(temp_dir, ARCHIVE)
-
- def execute_actions(self):
- count = 0
- total = len([actions for action in self.actions if action[0] is not ANNOTATE])
- try:
- for action, src, dst in self.actions:
- if action is not ANNOTATE:
- count += 1
- print("Action %2d(%2d): %s" % (count, total, ACTION_NAMES[action]))
- self.ACTION_METHODS[action](src, dst)
- print("BUILD DONE")
- except Exception as ex:
- print(str(ex))
- print("BUILD FAILED")
- sys.exit(1)
-
- def annotate(self, src, dst):
- self.print_header(src)
-
- def copyfile(self, src, dst):
- if src[0] == ARTIFACT:
- f = os.path.join(self.project_dir, self.get_artifact_src_name())
- t = os.path.join(self.artifact_dir, self.get_artifact_target_name())
- else:
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copyfile(f, t)
-
- def copydir(self, src, dst):
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copytree(f, t)
-
- def runpyscript(self, src, arg):
- try:
- script_path = os.path.join(self.project_dir, *src)
- self.print_src_dst(script_path, arg)
- if src[-1] == "setup.py":
- self.pushd(os.path.join(self.project_dir, "builddir"), None)
- success, msg = self.run_pyscript(script_path, [arg])
- self.popd(None, None)
- else:
- success, msg = self.run_pyscript(script_path, [self.project_dir, arg])
- if not success:
- raise Exception(msg)
- except Exception:
- pass
-
- def runpytest(self, src, dst):
- script_path = os.path.join(self.project_dir, *src)
- self.pushd(os.path.dirname(script_path), None)
- self.print_src_dst(src, os.path.abspath(dst))
- success, msg = self.run_pyscript(script_path, [], display_stderr=True)
- if not success:
- raise Exception(msg)
- self.popd(None, None)
-
- def runcmd(self, src, dst):
- t = os.path.join(self.project_dir, *dst)
- self.pushd(os.path.dirname(t), None)
- self.print_src_dst(src, t)
- success, msg = self.run_command([src, t])
- self.popd(None, None)
- if not success:
- raise Exception(msg)
-
- def pushd(self, src, dst):
- self.print_src_dst(os.getcwd(), os.path.abspath(src))
- self.cwd = os.getcwd()
- os.chdir(src)
-
- def popd(self, src, dst):
- self.print_src_dst(None, self.cwd)
- print(" dst: %s" % self.cwd)
- os.chdir(self.cwd)
-
- def run_pyscript(self, script, args=[], display_stderr=False):
- return self.run_command(["python", script] + args, display_stderr)
-
- def run_command(self, cmd, display_stderr=False):
- print('cmd:', cmd)
- if display_stderr:
- rc = subprocess.call(cmd)
- return rc == 0, ""
- else:
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out = p.communicate()
- print(out)
- if p.returncode == 0:
- return True, out[0]
- else:
- return False, out[1]
-
- def print_header(self, message):
- print("-------------------------------------------------------")
- print(" %s" % message)
- print("-------------------------------------------------------")
-
- def print_src_dst(self, src, dst):
- if src is not None:
- print(" src: %s" % src)
- if dst is not None:
- print(" dst: %s" % dst)
-
- def get_artifact_src_name(self):
- versionfile = os.path.join(self.project_dir, "source", "timelinelib", "meta", "version.py")
- f = open(versionfile, "r")
- text = f.read()
- lines = text.split("\n")
- for line in lines:
- if line[0:7] == "VERSION":
- break
- f.close()
- # VERSION = (0, 14, 0)
- line = line.split("(", 1)[1]
- line = line.split(")", 1)[0]
- major, minor, bug = line. split(", ")
- return "SetupTimeline%s%s%sPy2ExeWin32.exe" % (major, minor, bug)
-
- def get_artifact_target_name(self):
- return "%s-Win32Setup.exe" % self.archive.get_filename_version()
-
-def main():
- artifactdir = os.path.join(sys.path[0], "..")
- Target("win32Installer").build(parse_arguments(), artifactdir)
-
-
-def parse_arguments():
- parser = argparse.ArgumentParser()
- parser.add_argument("--revision", default="tip")
- return parser.parse_args()
-
-
-if __name__ == "__main__":
- main()
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/timelinetools/packaging/archive.py
--- a/tools/timelinetools/packaging/archive.py Sun Jul 06 09:06:44 2025 +0200
+++ b/tools/timelinetools/packaging/archive.py Sun Jul 06 09:10:31 2025 +0200
@@ -58,7 +58,7 @@
self._run_tool("execute-specs.py")
def create_zip_archive(self):
- self._clean_pyc_files()
+ self.clean_pyc_files()
zip_name = "%s.zip" % self.get_basename()
subprocess.check_call([
"zip",
@@ -69,6 +69,12 @@
], cwd=self.get_dirname())
return timelinetools.packaging.zipfile.ZipFile(self.get_dirname(), zip_name)
+ def clean_pyc_files(self):
+ for root, dirs, files in os.walk(self.get_path()):
+ for f in files:
+ if f.endswith(".pyc"):
+ os.remove(os.path.join(root, f))
+
def _run_tool(self, tool):
run_python_script_and_exit_if_fails(
os.path.join(
@@ -80,13 +86,9 @@
def _change_version_constant(self, constant, value):
_change_constant(self._get_version_path(), constant, value)
- self._clean_pyc_files()
- def _clean_pyc_files(self):
- for root, dirs, files in os.walk(self.get_path()):
- for f in files:
- if f.endswith(".pyc"):
- os.remove(os.path.join(root, f))
+ def change_constant(self, path, constant, value):
+ _change_constant(self.get_path(path), constant, value)
def _get_readme_path(self):
return os.path.join(self.get_path(), "README")
@@ -101,8 +103,8 @@
def _change_constant(path, constant, value):
_make_one_sub(
path,
- r"^%s\s*=\s*.*?$" % constant,
- "%s = %s" % (constant, value)
+ r"^%s(\s*)=(\s*).*?$" % constant,
+ r"%s\1=\2%s" % (constant, value)
)
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/timelinetools/packaging/path.py
--- a/tools/timelinetools/packaging/path.py Sun Jul 06 09:06:44 2025 +0200
+++ b/tools/timelinetools/packaging/path.py Sun Jul 06 09:10:31 2025 +0200
@@ -32,8 +32,8 @@
def get_basename(self):
return self._basename
- def get_path(self):
- return os.path.join(self._dirname, self._basename)
+ def get_path(self, *args):
+ return os.path.join(self._dirname, self._basename, *args)
def rename(self, new_basename):
os.rename(self.get_path(), os.path.join(self._dirname, new_basename))
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/build_win_setup_executable.py
--- a/tools/winbuildtools/build_win_setup_executable.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,374 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-This script is used to create a Timeline installation file for Windows
-
-Usage:
- Copy this file to a temporary directory and issue the command
- python build_win_setup_executable.py [version]
-
- If a version argument is given, it must be the name of a tagged version
- such as 2.10.0.
- If not given, the latest code is used.
-
- The path to the python version used is defined by the pragram constant
- PYTHON_HOME. If default isn't what you want you have to change it.
-
-Prerequisite:
- 1. Must run on a Windows machine
- 2. Mercurial (hg) must be installed
- 3. Python must be installed
- 4. Inno Setup (iscc.exe) must be installed
- 5. Dependent on files in Timeline repo:
- tools/execute-specs.py
- tools/generate-mo-files.py
- tools/winbuildtools/mod_paths.py
- tools/winbuildtools/mod_iss_timeline_version.py
- tools/winbuildtools/timeline.spec
- tools/winbuildtools/dist/icons/*
- tools/winbuildtools/dist/translations/*
-
-
-"""
-
-import os
-import subprocess
-from cmdlineparser import CmdLineParser, PositionalArg, NamedArg
-
-
-class BuildError(Exception):
- pass
-
-
-class Build:
-
- def __init__(self, python_home, build_version=None, keep_workspace=False):
- self.PYTHON = os.path.join(python_home, "python.exe")
- self.PIP = os.path.join(python_home, "pip.exe")
- self.PYINSTALLER = None
- self._build_version = build_version
- self._root_dir = os.path.dirname(os.path.realpath(__file__))
- self._keep_workspace = keep_workspace
- self._log_header("Input data")
- self._log(f"Python home : {python_home}")
- self._log(f"Build version : {self._build_version}")
- self._log(f"Keep workspace: {self._keep_workspace}")
-
- def _validate_prerequisite(self):
- def os_is_windows():
- if os.name == "nt":
- self._log("OS is windows")
- else:
- raise BuildError("This script only runs on Windows!")
-
- def mercurial_is_installed():
- if self._os_command("where", "hg").endswith("hg.exe"):
- self._log("Mercurial is installed")
- else:
- raise BuildError("Mercurial is not installed!")
-
- def python_installed():
- try:
- version = self._os_command(self.PYTHON, "-V")
- self._log(f"Python {version} is installed")
- except:
- raise BuildError("Python is not installed!")
-
- def iscc_is_installed():
- if self._os_command("where", "iscc").lower().endswith("iscc.exe"):
- self._log("ISCC is installed")
- else:
- raise BuildError("ISCC is not installed!")
-
- self._log_header("Validating Prerequisite")
- os_is_windows()
- mercurial_is_installed()
- python_installed()
- iscc_is_installed()
-
- def _create_workspace(self):
- self._create_dir("workspace")
- self._cd("workspace")
-
- def _os_command(self, *args):
- return subprocess.check_output(list(args)).decode("latin-1").strip()
-
- def _create_dir(self, dirname):
- os.mkdir(dirname)
-
- def _cd(self, dirname):
- if dirname == "root":
- path = self._root_dir
- else:
- path = os.path.join(self._root_dir, dirname)
- os.chdir(path)
- self._log(f"Directory changed to: {os.getcwd()}")
-
- def _remove_dir(self, dirname):
- os.rmdir(dirname)
-
- def _get_timeline_from_repo(self):
- """http://hg.code.sf.net/p/thetimelineproj/main"""
- self._log_header("Clone Timeline repo")
- os.system("hg clone http://hg.code.sf.net/p/thetimelineproj/main .")
- self._log_header("Get hg info")
- self._hg_node = self._get_hg_node()
- self._build_version_exists()
- self._hg_rev = self._get_hg_rev()
- self._log(f"Build version: {self._build_version}")
- self._log(f"Node : {self._hg_node}")
- self._log(f"Revision : {self._hg_rev}")
-
- def _build_version_exists(self):
- if self._build_version is not None:
- try:
- self._os_command(
- "hg", "log", "--rev", self._build_version, "--template", "exists"
- )
- self._log("Build version exists")
- self._os_command("hg", "update", "--rev", self._build_version)
- self._log("Project updated to build version")
- except:
- raise BuildError(f"unknown revision: {self._build_version}!")
-
- def _get_hg_node(self):
- return self._os_command("hg", "log", "--rev", ".", "--template", "{node}")
-
- def _get_hg_rev(self):
- return self._os_command("hg", "log", "--rev", ".", "--template", "{rev}")
-
- def _pip_install(self, package, version=None):
- self._log(f'Install {package} {version if version is not None else ""}')
- if version is not None:
- self._os_command(self.PIP, "install", "-v", f"{package}=={version}")
- else:
- self._os_command(self.PIP, "install", package)
-
- def _create_virtual_environment(self):
- def create_venv():
- self._os_command(self.PYTHON, "-m", "venv", "venv")
- self.PYTHON = os.path.join(
- self._root_dir, "workspace", "venv", "Scripts", "python.exe"
- )
- self.PIP = os.path.join(
- self._root_dir, "workspace", "venv", "Scripts", "pip.exe"
- )
-
- def upgrade_pip():
- self._log("Upgrade pip")
- self._os_command(self.PYTHON, "-m", "pip", "install", "--upgrade", "pip")
-
- def install_packages():
- packages = (
- ("humblewx", None),
- ("icalendar", None),
- ("Markdown", None),
- ("pyinstaller", None),
- ("wxPython", "4.1.1"),
- )
- for package, version in packages:
- self._pip_install(package, version)
- self.PYINSTALLER = os.path.join(
- self._root_dir, "workspace", "venv", "Scripts", "pyinstaller.exe"
- )
- self._log(self._os_command(self.PIP, "list"))
-
- self._log_header("Create virtual environment")
- create_venv()
- upgrade_pip()
- install_packages()
-
- def _run_tests(self):
- self._log_header("Run Tests")
- self._os_command(
- self.PYTHON, "tools/execute-specs.py", "--write-testlist", "testlist.txt"
- )
-
- def _generate_mo_file(self):
- self._log_header("Generate mo files")
- try:
- self._cd(os.path.join(self._root_dir, "workspace", "tools"))
- self._os_command(self.PYTHON, "generate-mo-files.py")
- finally:
- self._cd(os.path.join(self._root_dir, "workspace"))
-
- def _modify_source_files(self):
- self._log_header("Modify source files")
- self._cd(os.path.join(self._root_dir, "workspace", "tools", "winbuildtools"))
- self._os_command(self.PYTHON, "mod_paths.py", ".")
- self._log("paths.py modified")
- self._log(f"Revision: {self._build_version or self._hg_node}")
- self._os_command(
- self.PYTHON,
- "mod_iss_timeline_version.py",
- ".",
- self._build_version or self._hg_node,
- )
- self._log("version.py modified")
-
- def _create_timeline_executable(self):
- self._log_header("Create Timeline executable")
- self._cd(os.path.join(self._root_dir, "workspace", "tools", "winbuildtools"))
- self._os_command(self.PYINSTALLER, "timeline.spec")
-
- def _copy_files_to_dist(self):
- self._log("Copying icons and translations to dist")
- self._cd(os.path.join(self._root_dir, "workspace", "tools", "winbuildtools"))
- self._create_dir(".\\dist\\icons")
- self._create_dir(".\\dist\\icons\\event_icons")
- self._create_dir(".\\dist\\translations")
-
- self._os_command("xcopy", "/S", "..\\..\\icons\*.*", ".\\dist\icons\\*.*")
- self._log(f"Icons copied")
-
- for lang in (
- "ca",
- "de",
- "el",
- "es",
- "eu",
- "fr",
- "gl",
- "he",
- "it",
- "ko",
- "lt",
- "nl",
- "pl",
- "pt",
- "pt_BR",
- "ru",
- "sv",
- "tr",
- "vi",
- "zh_CN",
- ):
- self._os_command(
- "xcopy",
- "/S",
- f"..\\..\\translations\\{lang}\\*.*",
- f".\\dist\\translations\\{lang}\\*.*",
- )
- self._log(f"{lang} translations copied")
-
- def _create_distrubtable(self):
- self._log_header("Create distributable")
- self._copy_files_to_dist()
- self._cd(
- os.path.join(self._root_dir, "workspace", "tools", "winbuildtools", "inno")
- )
- self._os_command("iscc.exe", "timeline2Win32.iss")
-
- def _move_result(self):
- self._log_header("Move result")
- self._cd(
- os.path.join(
- self._root_dir, "workspace", "tools", "winbuildtools", "inno", "out"
- )
- )
- self._os_command("xcopy", "/Y", "*.exe", f"{self._root_dir}\\*.*")
- self._log("Resulting exe-file moved to root dir.")
-
- def _log_header(self, text):
- print("-" * 50)
- print(f" {text}")
- print("-" * 50)
-
- def _log(self, text):
- print(f" {text}")
-
- def _abort_message(self, ex):
- self._log_header("Build aborted:")
- self._log(ex)
-
- def _clean_up(self):
- self._log_header("Clean up")
- self._cd("root")
- if os.path.isdir("workspace"):
- os.system("rmdir /S /Q workspace")
- self._log("workspace directory removed")
- else:
- self._log("No workspace found")
- if os.path.isdir("__pycache__"):
- os.system("rmdir /S /Q __pycache__")
- self._log("__pycache__ directory removed")
-
- def run(self):
- try:
- self._clean_up()
- self._validate_prerequisite()
- self._create_workspace()
- self._get_timeline_from_repo()
- self._create_virtual_environment()
- self._run_tests()
- self._generate_mo_file()
- self._modify_source_files()
- self._create_timeline_executable()
- self._create_distrubtable()
- self._move_result()
- except BuildError as ex:
- self._abort_message(ex)
- finally:
- if not self._keep_workspace:
- self._clean_up()
-
-
-def print_help():
- print(
- """
-Usage:
-
- python build_win_setup_executable.py [options]
-
-Options:
-
- -h, --help Print help
- -p dirname, --python-home dirname Path to dir where python.exe can be found. Default = c:\pgm\python396
- -t tagname, --tag tagname Tagged version to build. Default = latest code
- -k, --keep-workspace Kepp the workspace directory when finished
-
- """
- )
-
-
-if __name__ == "__main__":
- DEFAULT_PYTHON_HOME = "c:\\pgm\\Python396"
- specs = [
- NamedArg(name="t", long_name="tag", has_value=True),
- NamedArg(name="k", long_name="keep-workspace"),
- NamedArg(name="p", long_name="python-home", has_value=True),
- NamedArg(name="h", long_name="help"),
- ]
- parser = CmdLineParser(specs)
- if parser.help:
- print_help()
- else:
- build_version = parser.tag_value
- python_home = (
- parser.python_home_value
- if parser.python_home_value
- else DEFAULT_PYTHON_HOME
- )
- Build(
- python_home=python_home,
- build_version=build_version,
- keep_workspace=parser.keep_workspace,
- ).run()
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/buildwinsetup.cmd
--- a/tools/winbuildtools/buildwinsetup.cmd Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-@echo off
-
-echo --------------------------------------
-echo Revision: %1
-echo --------------------------------------
-
-
-echo --------------------------------------
-echo Remove old build and dist directories
-echo --------------------------------------
-rmdir /S /Q build >> nul
-rmdir /S /Q dist >> nul
-del /S /Q inno\out >> nul
-
-echo --------------------------------------
-echo Create translations
-echo --------------------------------------
-pushd ..
-python generate-mo-files.py
-popd
-
-echo --------------------------------------
-echo Modify paths.py
-echo --------------------------------------
-python mod_paths.py .
-
-echo --------------------------------------
-echo Modify version file and iss file
-echo --------------------------------------
-python mod_iss_timeline_version.py . %1
-
-echo --------------------------------------
-echo Building distribution
-echo --------------------------------------
-pyinstaller timeline.spec
-
-echo --------------------------------------
-echo Copying icons and translations to dist
-echo --------------------------------------
-mkdir .\dist\icons\event_icons
-mkdir .\dist\translations
-xcopy /S ..\..\icons\*.* .\dist\icons\*.*
-xcopy /S ..\..\translations\ca\*.* .\dist\translations\ca\*.*
-xcopy /S ..\..\translations\de\*.* .\dist\translations\de\*.*
-xcopy /S ..\..\translations\el\*.* .\dist\translations\el\*.*
-xcopy /S ..\..\translations\es\*.* .\dist\translations\es\*.*
-xcopy /S ..\..\translations\eu\*.* .\dist\translations\eu\*.*
-xcopy /S ..\..\translations\fr\*.* .\dist\translations\fr\*.*
-xcopy /S ..\..\translations\gl\*.* .\dist\translations\gl\*.*
-xcopy /S ..\..\translations\he\*.* .\dist\translations\he\*.*
-xcopy /S ..\..\translations\it\*.* .\dist\translations\it\*.*
-xcopy /S ..\..\translations\ko\*.* .\dist\translations\ko\*.*
-xcopy /S ..\..\translations\lt\*.* .\dist\translations\lt\*.*
-xcopy /S ..\..\translations\nl\*.* .\dist\translations\nl\*.*
-xcopy /S ..\..\translations\pl\*.* .\dist\translations\pl\*.*
-xcopy /S ..\..\translations\pt\*.* .\dist\translations\pt\*.*
-xcopy /S ..\..\translations\pt_BR\*.* .\dist\translations\pt_BR\*.*
-xcopy /S ..\..\translations\ru\*.* .\dist\translations\ru\*.*
-xcopy /S ..\..\translations\sv\*.* .\dist\translations\sv\*.*
-xcopy /S ..\..\translations\tr\*.* .\dist\translations\tr\*.*
-xcopy /S ..\..\translations\vi\*.* .\dist\translations\vi\*.*
-xcopy /S ..\..\translations\zh_CH\*.* .\dist\translations\zh_CH\*.*
-
-
-echo --------------------------------------
-echo Create distributable
-echo --------------------------------------
-pushd inno
-iscc.exe timeline2Win32.iss
-popd
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/cmdlineparser.py
--- a/tools/winbuildtools/cmdlineparser.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-The CmdLineParser reads the command line input and stores the separate
-parts as properties, according to the given specification.
-
-A command line has two types of arguments
- - Positional argument
- - Named argument
-
-Arguments are separated with on or more spaces.
-
-A named argument is always prefixed with a dash (-) which a named argument isn't.
-
-A named argument has two types. One with a single dash prefix and one with a double
-dash prefix. The first one is intended for single character names and the second
-one for long names. Both can have a specified value.
-
-If a name contains a dash character, the attribute name of the parser will be the same
-but with the dash replaced with an underscore character.
-
-The name 'invalid-args' is reserved for the parser and cannot be used as a command line
-argument.
-
-By default, the input argument list is taken from sys.argv[1:], but it can be
-specified as an argument to the CmdLineParser constructor.
-
-Example:
- cmd line: pgm.exe -v --log-name mylog filename
-
- pgm.exe The name of the executed program
- -v A named argument without a value
- --log-name mylog A named argument with the value mylog
- filename A positional argument
-
-Usage:
- # Define valid arguments
- specs = [
- PositionalArg(name="filename"),
- NamedArg(name="v", long_name="verbose"),
- NamedArg(long_name="log-name", has_value=True),
- ]
-
- # Create the parser and process the command line input
- parser = CmdLineParser(specs)
-
- # Use input data
- if parser.verbose:
- print("Verbose mode")
- print(f"logfile : {parser.log_name}")
- print(f"filename: {parser.filename}")
-
-"""
-
-import sys
-
-
-class Arg:
- def __init__(self, name="", long_name="", has_value=False):
- self._name = name
- self._long_name = long_name
- self._has_value = has_value
-
- @property
- def name(self):
- return self._name
-
- @property
- def long_name(self):
- return self._long_name
-
- @property
- def has_value(self):
- return self._has_value
-
-
-class PositionalArg(Arg):
- def __init__(self, name):
- Arg.__init__(self, name)
-
-
-class NamedArg(Arg):
- def __init__(self, name="", long_name="", has_value=None):
- Arg.__init__(self, name, long_name, has_value)
-
-
-class CmdLineParser:
- """
- No arguments given on the command line
-
- >>> sys.argv = ["pgm.exe"]
- >>> specs = [
- ... PositionalArg(name="filename"),
- ... NamedArg(name="v", long_name="verbose"),
- ... NamedArg(name="l", long_name="log-name", has_value=True),]
- >>> parser = CmdLineParser(specs)
- >>> parser.filename
- False
- >>> parser.log_name
- False
- >>> parser.l
- False
- >>> parser.verbose
- False
- >>> parser.v
- False
- >>> parser.invalid_args
- []
-
- Command line arguments given
-
- >>> sys.argv = ["pgm.exe", "-v", "--log-name", "mylog", "myfile"]
- >>> parser = CmdLineParser(specs)
- >>> parser.filename
- 'myfile'
- >>> parser.v and parser.verbose
- True
- >>> parser.log_name_value
- 'mylog'
- >>> parser.l_value
- 'mylog'
-
- Invalid Command line arguments given
-
- >>> sys.argv = ["pgm.exe", "-vv", "--log-name", "mylog", "myfile"]
- >>> parser = CmdLineParser(specs)
- >>> len(parser.invalid_args)
- 1
- >>> parser.invalid_args[0]
- '-vv'
-
- Same arg given twice
-
- >>> sys.argv = ["pgm.exe", "--log-name", "mylog2", "--log-name", "mylog2", "myfile1", "myfile2"]
- >>> parser = CmdLineParser(specs)
-
- If named args are duplicated the last one is used
-
- >>> parser.log_name_value
- 'mylog2'
-
- Positional arguments are always assigned from left to right.
-
- >>> parser.filename
- 'myfile1'
- >>> parser.invalid_args[0]
- 'myfile2'
-
- The 'name' agument is not mandatory for NamedArg
-
- >>> sys.argv = ["pgm.exe", "--verbose", "myfile1"]
- >>> specs = [
- ... PositionalArg(name="filename"),
- ... NamedArg(long_name="verbose")]
- >>> parser = CmdLineParser(specs)
- >>> parser.verbose
- True
- """
-
- def __init__(self, specs, args=None):
- self._positional_specs = [s for s in specs if isinstance(s, PositionalArg)]
- self._named_specs = [s for s in specs if isinstance(s, NamedArg)]
- self._args = args or sys.argv[1:]
- self._invalid_args = []
- self._positional_args = []
- self._init_args()
- self._parse()
-
- @property
- def invalid_args(self):
- return self._invalid_args
-
- def _init_args(self):
- for spec in self._positional_specs:
- spec.attr_name = self._attr_name(spec.name)
- setattr(self, spec.attr_name, False)
- for spec in self._named_specs:
- self._create_attribute(spec)
- self._set_attribute(spec, False)
- self._set_attribute_value(spec, None)
-
- def _create_attribute(self, spec):
- if len(spec.name) > 0:
- spec.attr_name = self._attr_name(spec.name)
- if len(spec.long_name) > 0:
- spec.attr_long_name = self._attr_name(spec.long_name)
-
- def _set_attribute(self, spec, value):
- if len(spec.name) > 0:
- setattr(self, spec.attr_name, value)
- if len(spec.long_name) > 0:
- setattr(self, spec.attr_long_name, value)
-
- def _set_attribute_value(self, spec, value):
- if len(spec.name) > 0:
- setattr(self, f"{spec.attr_name}_value", value)
- if len(spec.long_name) > 0:
- setattr(self, f"{spec.attr_long_name}_value", value)
-
- def _attr_name(self, name):
- return name.replace("-", "_")
-
- @property
- def _next_arg(self):
- return self._args[0]
-
- @property
- def _more_args_exists(self):
- return len(self._args) > 0
-
- @property
- def _next_arg_is_named(self):
- return self._next_arg.startswith("-")
-
- @property
- def _next_arg_is_positional(self):
- return not self._next_arg_is_named
-
- def _remove_first_arg(self):
- self._args = self._args[1:]
-
- def _parse(self):
- while self._more_args_exists:
- if self._next_arg_is_named:
- self._parse_named_arg()
- else:
- self._parse_positional_arg()
-
- def _parse_named_arg(self):
- spec = self._find_spec()
- if spec is None:
- self._invalid_args.append(self._next_arg)
- self._remove_first_arg()
- else:
- self._set_attribute(spec, True)
- self._remove_first_arg()
- if spec.has_value and self._more_args_exists:
- if self._next_arg_is_positional:
- self._set_attribute_value(spec, self._next_arg)
- self._remove_first_arg()
-
- def _find_spec(self):
- for spec in self._named_specs:
- if (
- f"-{spec.name}" == self._next_arg
- or f"--{spec.long_name}" == self._next_arg
- ):
- return spec
-
- def _parse_positional_arg(self):
- index = len(self._positional_args)
- try:
- spec = self._positional_specs[index]
- setattr(self, spec.attr_name, self._next_arg)
- self._positional_args.append(self._next_arg)
- except IndexError:
- self._invalid_args.append(self._next_arg)
- self._remove_first_arg()
-
-
-if __name__ == "__main__":
- import doctest
-
- doctest.testmod()
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/inno/timeline.iss
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/winbuildtools/inno/timeline.iss Sun Jul 06 09:10:31 2025 +0200
@@ -0,0 +1,66 @@
+; Script generated by the Inno Setup Script Wizard.
+; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
+
+[Setup]
+AppName=Timeline
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+;
+; Before running this script ...
+; The two lines below must be uncommented and text changed to reflect
+; the version number of the executable to be built.
+;
+AppVerName=Timeline 2.1.0
+OutputBaseFilename=timeline-2.1.0-beta-4b501487562b-2019-11-26-Win32Setup
+;
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+AppPublisher=Rickard Lindberg <ricli85@gmail.com>
+AppPublisherURL=http://thetimelineproj.sourceforge.net/
+AppSupportURL=http://thetimelineproj.sourceforge.net/
+AppUpdatesURL=http://thetimelineproj.sourceforge.net/
+DefaultDirName={pf}\Timeline
+DefaultGroupName=Timeline
+SourceDir=.\
+LicenseFile=..\..\..\COPYING
+InfoBeforeFile=WINSTALL
+OutputDir=out
+Compression=lzma
+SolidCompression=yes
+DisableDirPage=no
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+
+[Tasks]
+Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+Name: "startmenu"; Description: "Create a start menu"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+
+[Files]
+Source: "..\dist\timeline.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
+Source: "..\dist\icons\*"; DestDir: "{app}\icons"; Flags: ignoreversion recursesubdirs
+Source: "..\dist\translations\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs
+
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+;
+; Before running this script ...
+; You must check to see if there are any more po-files to add
+;
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+[Icons]
+Name: "{commondesktop}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; Tasks: desktopicon
+Name: "{group}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; WorkingDir: "{app}"; Tasks: startmenu
+
+
+
+
+[Run]
+Filename: "{app}\timeline.exe"; Description: "{cm:LaunchProgram,Timeline}"; Flags: shellexec postinstall skipifsilent;
+
+
+[UninstallDelete]
+Type: files; Name: "{win}\uninstall\MYPROG.INI"
+
+
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/inno/timeline2Win32.iss
--- a/tools/winbuildtools/inno/timeline2Win32.iss Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-; Script generated by the Inno Setup Script Wizard.
-; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
-
-[Setup]
-AppName=Timeline
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-;
-; Before running this script ...
-; The two lines below must be uncommented and text changed to reflect
-; the version number of the executable to be built.
-;
-AppVerName=Timeline 2.1.0
-OutputBaseFilename=timeline-2.1.0-beta-4b501487562b-2019-11-26-Win32Setup
-;
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-AppPublisher=Rickard Lindberg <ricli85@gmail.com>
-AppPublisherURL=http://thetimelineproj.sourceforge.net/
-AppSupportURL=http://thetimelineproj.sourceforge.net/
-AppUpdatesURL=http://thetimelineproj.sourceforge.net/
-DefaultDirName={pf}\Timeline
-DefaultGroupName=Timeline
-SourceDir=.\
-LicenseFile=..\..\..\COPYING
-InfoBeforeFile=WINSTALL
-OutputDir=out
-Compression=lzma
-SolidCompression=yes
-DisableDirPage=no
-
-[Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
-
-
-[Tasks]
-Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
-Name: "startmenu"; Description: "Create a start menu"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
-
-[Files]
-Source: "..\dist\timeline.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
-Source: "..\dist\icons\*"; DestDir: "{app}\icons"; Flags: ignoreversion recursesubdirs
-Source: "..\dist\translations\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs
-
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-;
-; Before running this script ...
-; You must check to see if there are any more po-files to add
-;
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-
-[Icons]
-Name: "{commondesktop}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; Tasks: desktopicon
-Name: "{group}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; WorkingDir: "{app}"; Tasks: startmenu
-
-
-
-
-[Run]
-Filename: "{app}\timeline.exe"; Description: "{cm:LaunchProgram,Timeline}"; Flags: shellexec postinstall skipifsilent;
-
-
-[UninstallDelete]
-Type: files; Name: "{win}\uninstall\MYPROG.INI"
-
-
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/jenkins-build-local.py
--- a/tools/winbuildtools/jenkins-build-local.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,306 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-Our Jenkins server is about to be retired.
-
-That means that the Windows installation executable can no longer
-be built from Jenkins (calling a build slave).
-
-This script is intended to be run on the local Windows box that
-has the Jenkins slave installation to build the Windows installation
-executable file.
-
-"""
-
-import sys
-import os
-import subprocess
-
-
-class BuildError(Exception):
- pass
-
-
-def get_build_version_from_input():
- """
- The first argument, if any, is the tagged version t obe built.
- For beta releases, the hg rev number is used.
-
- >>> get_build_version_from_input()
-
- >>> sys.argv.insert(1, '2.11.0')
- >>> get_build_version_from_input()
- '2.11.0'
-
- """
- try:
- return sys.argv[1]
- except IndexError:
- pass
-
-
-def is_windows():
- return os.name == 'nt'
-
-
-def hg_command(*args):
- """
- >>> hg_command('showconfig', 'paths.default')
- 'http://hg.code.sf.net/p/thetimelineproj/main'
-
- """
- try:
- if is_windows():
- return subprocess.check_output(['hg'] + list(args)).decode('latin-1').strip()
- else:
- return subprocess.check_output(['hg'] + list(args)).decode().strip()
- except Exception as ex:
- raise BuildError(str(ex))
-
-
-def os_command(*args):
- try:
- if is_windows():
- return subprocess.check_output(list(args)).decode('latin-1').strip()
- else:
- return subprocess.check_output(list(args)).decode().strip()
- except Exception as ex:
- raise BuildError(str(ex))
-
-
-def get_hg_default_path():
- return hg_command('showconfig', 'paths.default')
-
-
-def hg_pull():
- hg_command('pull', '--rev', 'default')
-
-
-def hg_update():
- hg_command('update', '--clean', '--rev', 'default')
-
-
-def hg_purge():
- hg_command('--config', 'extensions.purge=', 'clean', '--all')
-
-
-def get_hg_node():
- return hg_command('log', '--rev', '.', '--template', '{node}')
-
-
-def get_hg_rev():
- return hg_command('log', '--rev', '.', '--template', '{rev}')
-
-
-def verify_version(build_version, node):
- """
- >>> verify_version('2.10.0', 'a821cfd8cbe1f28ac4d01ffe02746632161db27b')
- 'exists'
-
- >>> verify_version(None, 'a821cfd8cbe1f28ac4d01ffe02746632161db27b')
- 'exists'
-
- >>> verify_version(None, 'abcd')
- Traceback (most recent call last):
- ...
- jenkins-build-local.BuildError: unknown revision: abcd!
- """
- try:
- return hg_command('log', '--rev', build_version or node, '--template', 'exists')
- except:
- raise BuildError(f'unknown revision: {build_version or node}!')
-
-
-def log_changesets(build_revision, node):
- return hg_command(
- 'log',
- '--template',
- "<changeset node='{node}' author='{author|xmlescape}' rev='{rev}' date='{date}'><msg>{desc|xmlescape}</msg>{file_adds % '<addedFile>{file|xmlescape}</addedFile>'}{file_dels % '<deletedFile>{file|xmlescape}</deletedFile>'}{files % '<file>{file|xmlescape}</file>'}<parents>{parents}</parents></changeset>\n",
- '--rev',
- f"ancestors('default') and not ancestors({build_revision or node})",
- '--encoding',
- 'UTF-8',
- '--encodingmode',
- 'replace')
-
-
-def upgrade_pip():
- os_command('python.exe', '-m', 'pip', 'install', '--upgrade', 'pip')
-
-
-def display_python_version():
- log_subheader('Python Version')
- log(os_command('python', '-V'))
-
-
-def create_virtual_environment():
- log_subheader('Crate virtual environment')
- log(os_command('python', '-m', 'venv', 'venv'))
-
-
-def upgrade_pip():
- log_subheader('Upgrade pip')
- os.system('venv\\Scripts\\python -m pip install --upgrade pip')
-
-
-def install_wxpython():
- log_subheader('Install wwPython')
- os_command('venv\\Scripts\\pip', 'install', '--force-reinstall', '-v', 'wxPython==4.1.1')
-
-
-def install_humblewx():
- log_subheader('Install humblewx')
- os_command('venv\Scripts\\pip', 'install', 'humblewx')
-
-
-def install_icalendar():
- log_subheader('Install icalendar')
- os_command('venv\\Scripts\\pip', 'install', 'icalendar')
-
-
-def install_markdown():
- log_subheader('Install Markdown')
- os_command('venv\\Scripts\\pip', 'install', 'Markdown')
-
-
-def run_tests():
- log_subheader('Run Tests')
- os_command('venv\\Scripts\\python', 'tools/execute-specs.py', '--write-testlist', 'testlist.txt')
-
-
-def build(build_version, node):
- log_subheader(f'Start Build {build_version or node}')
-
- os.chdir('tools')
- os.system('..\\venv\\Scripts\python generate-mo-files.py')
- log('mo file generated')
-
- os.chdir('winbuildtools')
- os.system('..\\..\\venv\\Scripts\python mod_paths.py .')
- log('paths modified')
-
- os.system(f'..\\..\\venv\\Scripts\python mod_iss_timeline_version.py . {build_version or node}')
- log('version file modified')
-
- log_subheader('Remove old build directory')
- os.system('rmdir /S /Q build >> nul')
- log_subheader('Remove old dist directory')
- os.system('rmdir /S /Q dist >> nul')
- log_subheader('Remove old out directory')
- os.system('del /S /Q inno\out >> nul')
-
- log_subheader('pyinstaller')
- os.system('pyinstaller timeline.spec')
-
- log_subheader('Copying icons and translations to dist')
- os.system('mkdir .\\dist\\icons\\event_icons')
- os.system('mkdir .\\dist\\translations')
- os.system('xcopy /S ..\\..\\icons\*.* .\dist\icons\*.*')
- os.system('xcopy /S ..\\..\\translations\ca\*.* .\dist\\translations\ca\*.*')
- os.system('xcopy /S ..\\..\\translations\de\*.* .\dist\\translations\de\*.*')
- os.system('xcopy /S ..\\..\\translations\el\*.* .\dist\\translations\el\*.*')
- os.system('xcopy /S ..\\..\\translations\es\*.* .\dist\\translations\es\*.*')
- os.system('xcopy /S ..\\..\\translations\eu\*.* .\dist\\translations\eu\*.*')
- os.system('xcopy /S ..\\..\\translations\fr\*.* .\dist\\translations\fr\*.*')
- os.system('xcopy /S ..\\..\\translations\gl\*.* .\dist\\translations\gl\*.*')
- os.system('xcopy /S ..\\..\\translations\he\*.* .\dist\\translations\he\*.*')
- os.system('xcopy /S ..\\..\\translations\it\*.* .\dist\\translations\it\*.*')
- os.system('xcopy /S ..\\..\\translations\ko\*.* .\dist\\translations\ko\*.*')
- os.system('xcopy /S ..\\..\\translations\lt\*.* .\dist\\translations\lt\*.*')
- os.system('xcopy /S ..\\..\\translations\nl\*.* .\dist\\translations\nl\*.*')
- os.system('xcopy /S ..\\..\\translations\pl\*.* .\dist\\translations\pl\*.*')
- os.system('xcopy /S ..\\..\\translations\pt\*.* .\dist\\translations\pt\*.*')
- os.system('xcopy /S ..\\..\\translations\pt_BR\*.* .\dist\\translations\pt_BR\*.*')
- os.system('xcopy /S ..\\..\\translations\ru\*.* .\dist\\translations\ru\*.*')
- os.system('xcopy /S ..\\..\\translations\sv\*.* .\dist\\translations\sv\*.*')
- os.system('xcopy /S ..\\..\\translations\tr\*.* .\dist\\translations\tr\*.*')
- os.system('xcopy /S ..\\..\\translations\vi\*.* .\dist\\translations\vi\*.*')
- os.system('xcopy /S ..\\..\\translations\zh_CH\*.* .\dist\\translations\zh_CH\*.*')
-
- log('Create distributable')
- os.chdir('inno')
- os.system('iscc.exe timeline2Win32.iss')
- log_subheader('DONE')
-
-
-def log_header():
- print('-' * 70)
- print(' jenkins-build-local.py')
- print('-' * 70)
-
-
-def log_subheader(text):
- print(f'----({text})------')
-
-
-def log(text):
- print(f' {text}')
-
-
-def main():
- log_header()
- hg_pull()
- hg_update()
- hg_purge()
- build_version = get_build_version_from_input()
- default_path = get_hg_default_path()
- node = get_hg_node()
- revision = get_hg_rev()
- log(f'Build version: {build_version}')
- log(f'Defult Path : {default_path}')
- log(f'Node : {node}')
- log(f'Revision : {revision}')
-
- log_subheader('Verify version')
- verify_version(build_version, node)
- log('Verified that revision exists')
-
- log_subheader('Log changesets')
- changesets = log_changesets(build_version, node)
- print(changesets)
-
- display_python_version()
- create_virtual_environment()
- upgrade_pip()
-
- install_wxpython()
- install_humblewx()
- install_icalendar()
- install_markdown()
- run_tests()
-
- os.system('venv\\Scripts\pip list')
-
- build(build_version, node)
-
-
-if __name__ == '__main__':
- if 'doctest' in sys.argv:
- import doctest
-
- sys.argv = sys.argv[:-1]
- doctest.testmod()
- else:
- try:
- main()
- except BuildError as ex:
- log('Build aborted')
- log(ex)
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/mod_iss_timeline_version.py
--- a/tools/winbuildtools/mod_iss_timeline_version.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-import sys
-import os
-import subprocess
-
-
-USAGE = """
- Usage:
- python mod_iss_timeline_version.py project-tools-dir revision
-"""
-
-
-def get_hash_and_id(revision):
- try:
- return subprocess.check_output([
- "hg", "id",
- "-r", revision,
- ]).decode("utf-8").strip().split(" ", 1)
- except subprocess.CalledProcessError as e:
- print("ERROR:", str(e))
- raise
-
-
-def get_revision_date(revision):
- try:
- return subprocess.check_output([
- "hg", "log",
- "-r", revision,
- "--template", "{date|shortdate}",
- ]).decode("utf-8").strip()
- except subprocess.CalledProcessError as e:
- print("ERROR:", str(e))
- raise
-
-
-def get_version(versionfile):
- with open(versionfile, "r") as f:
- text = f.read()
- lines = text.split("\n")
- for line in lines:
- if line[0:7] == "VERSION":
- break
- line = line.split("(", 1)[1]
- line = line.split(")", 1)[0]
- major, minor, bug = line. split(", ")
- app_ver_name = "Timeline %s.%s.%s" % (major, minor, bug)
- revision = sys.argv[2]
- print("Revision:", revision)
- hash_value, rev_id = get_hash_and_id(sys.argv[2])
- revision_date = get_revision_date(sys.argv[2])
- if rev_id == 'tip':
- output_base_filename = "timeline-%s.%s.%s-beta-%s-%s-Win32Setup" % (major, minor, bug, hash_value, revision_date)
- type = 'TYPE_BETA'
- else:
- output_base_filename = "timeline-%s.%s.%s-Win32Setup" % (major, minor, bug)
- type = 'TYPE_FINAL'
- with open(versionfile, "r") as f:
- text = f.read()
- text = text.replace('TYPE = TYPE_DEV', f'TYPE = {type}')
- text = text.replace('REVISION_HASH = ""', f'REVISION_HASH = "{hash_value}"')
- text = text.replace('REVISION_DATE = ""', f'REVISION_DATE = "{revision_date}"')
- with open(versionfile, "w") as f:
- f.write(text)
- print("[INFO] Version found: %s" % app_ver_name)
- print("[INFO] Filename: %s" % output_base_filename)
- return app_ver_name, output_base_filename
-
-
-def modify_iss_file(target, app_ver_name, output_base_filename):
- with open(target, "r") as f:
- text = f.read()
- with open(target, "w") as f:
- lines = text.split("\n")
- for line in lines:
- if line[0:11] == "AppVerName=":
- line = "AppVerName=%s" % app_ver_name
- f.write(line + "\n")
- print("[INFO] Iss file version line: %s" % line)
- elif line[0:19] == "OutputBaseFilename=":
- line = "OutputBaseFilename=%s" % output_base_filename
- f.write(line + "\n")
- print("[INFO] Iss base filename line: %s" % line)
- else:
- f.write(line + "\n")
-
-
-def main():
- project_dir = sys.argv[1]
- target = os.path.join(project_dir, "inno", "timeline2Win32.iss")
- versionfile_path = os.path.join(project_dir, "..", "..", "source", "timelinelib", "meta", "version.py")
- print("Script: mod2_timeline_iss_win32.py")
- print("Target:", target)
- print("Version:", versionfile_path)
- if not os.path.exists(target):
- print("[ERROR] Can't find target file: %s" % target)
- return
- if not os.path.exists(versionfile_path):
- print("[ERROR] Can't find version file: %s" % versionfile_path)
- return
- app_ver_name, output_base_filename = get_version(versionfile_path)
- modify_iss_file(target, app_ver_name, output_base_filename)
-
-
-if __name__ == "__main__":
- if len(sys.argv) != 3:
- print(USAGE)
- else:
- if not os.path.exists(sys.argv[1]):
- print(USAGE)
- print("[ERROR] Can't find project root dir: %s" % sys.argv[1])
- else:
- main()
diff -r 7bdcd3d4dc9a -r dd4c4b92daa3 tools/winbuildtools/mod_paths.py
--- a/tools/winbuildtools/mod_paths.py Sun Jul 06 09:06:44 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-import sys
-import os
-
-
-def main():
- project_dir = sys.argv[1]
- path = os.path.join(
- project_dir, "..", "..", "source", "timelinelib", "config", "paths.py"
- )
- text = None
- with open(path) as f:
- text = f.read()
- collector = []
- for line in text.split("\n"):
- if line.strip().startswith("_ROOT ="):
- line = "_ROOT = '.'"
- collector.append(line.strip())
- text = "\n".join(collector)
- with open(path, "w") as f:
- f.write(text)
- print("paths.py modified")
- print(text)
-
-
-main()
changeset: 7991:ca9470a3fae4
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sun Jul 06 09:02:16 2025 +0200
summary: Document change in Windows installer.
diff -r af1960bd1ad4 -r ca9470a3fae4 documentation/changelog.rst
--- a/documentation/changelog.rst Sun Jul 06 08:27:09 2025 +0200
+++ b/documentation/changelog.rst Sun Jul 06 09:02:16 2025 +0200
@@ -34,6 +34,10 @@
* ``Corrupted icon file causes exception``
Use a default icon when icon file is corrupted and disable error dialog.
+Windows installer:
+
+* The Windows installer is now built on Linux/Wine and only for 64 bit platforms.
+
Version 2.10.0
--------------
changeset: 7990:af1960bd1ad4
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sun Jul 06 08:27:09 2025 +0200
summary: Win64Setup is no longer experimental.
diff -r 3794bad33bc3 -r af1960bd1ad4 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Sun Jul 06 08:23:28 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Sun Jul 06 08:27:09 2025 +0200
@@ -56,7 +56,7 @@
"AppVerName",
f"Timeline {archive.get_version_number_string()}"
)
- exe_name = f"{archive.get_filename_version()}-Win64Setup-Experimental"
+ exe_name = f"{archive.get_filename_version()}-Win64Setup"
archive.change_constant(
"tools/winbuildtools/inno/timeline.iss",
"OutputBaseFilename",
changeset: 7989:3794bad33bc3
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sun Jul 06 08:23:28 2025 +0200
summary: Rename timeline2Win32.iss to timeline.iss.
diff -r c6f07716d723 -r 3794bad33bc3 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Sun Jul 06 08:20:45 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Sun Jul 06 08:23:28 2025 +0200
@@ -52,13 +52,13 @@
sys.exit("ERROR: Could not find source zip.")
archive = timelinetools.packaging.zipfile.ZipFile(".", zips[0]).extract_to(tempdir)
archive.change_constant(
- "tools/winbuildtools/inno/timeline2Win32.iss",
+ "tools/winbuildtools/inno/timeline.iss",
"AppVerName",
f"Timeline {archive.get_version_number_string()}"
)
exe_name = f"{archive.get_filename_version()}-Win64Setup-Experimental"
archive.change_constant(
- "tools/winbuildtools/inno/timeline2Win32.iss",
+ "tools/winbuildtools/inno/timeline.iss",
"OutputBaseFilename",
exe_name
)
@@ -87,7 +87,7 @@
subprocess.check_call([
"wine",
f"{environ['INNO_DIR']}\\ISCC.exe",
- "inno/timeline2Win32.iss",
+ "inno/timeline.iss",
], cwd=archive.get_path("tools/winbuildtools"))
subprocess.check_call([
"wineserver",
diff -r c6f07716d723 -r 3794bad33bc3 tools/winbuildtools/inno/timeline.iss
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/winbuildtools/inno/timeline.iss Sun Jul 06 08:23:28 2025 +0200
@@ -0,0 +1,66 @@
+; Script generated by the Inno Setup Script Wizard.
+; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
+
+[Setup]
+AppName=Timeline
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+;
+; Before running this script ...
+; The two lines below must be uncommented and text changed to reflect
+; the version number of the executable to be built.
+;
+AppVerName=Timeline 2.1.0
+OutputBaseFilename=timeline-2.1.0-beta-4b501487562b-2019-11-26-Win32Setup
+;
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+AppPublisher=Rickard Lindberg <ricli85@gmail.com>
+AppPublisherURL=http://thetimelineproj.sourceforge.net/
+AppSupportURL=http://thetimelineproj.sourceforge.net/
+AppUpdatesURL=http://thetimelineproj.sourceforge.net/
+DefaultDirName={pf}\Timeline
+DefaultGroupName=Timeline
+SourceDir=.\
+LicenseFile=..\..\..\COPYING
+InfoBeforeFile=WINSTALL
+OutputDir=out
+Compression=lzma
+SolidCompression=yes
+DisableDirPage=no
+
+[Languages]
+Name: "english"; MessagesFile: "compiler:Default.isl"
+
+
+[Tasks]
+Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+Name: "startmenu"; Description: "Create a start menu"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+
+[Files]
+Source: "..\dist\timeline.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
+Source: "..\dist\icons\*"; DestDir: "{app}\icons"; Flags: ignoreversion recursesubdirs
+Source: "..\dist\translations\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs
+
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+;
+; Before running this script ...
+; You must check to see if there are any more po-files to add
+;
+;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+[Icons]
+Name: "{commondesktop}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; Tasks: desktopicon
+Name: "{group}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; WorkingDir: "{app}"; Tasks: startmenu
+
+
+
+
+[Run]
+Filename: "{app}\timeline.exe"; Description: "{cm:LaunchProgram,Timeline}"; Flags: shellexec postinstall skipifsilent;
+
+
+[UninstallDelete]
+Type: files; Name: "{win}\uninstall\MYPROG.INI"
+
+
diff -r c6f07716d723 -r 3794bad33bc3 tools/winbuildtools/inno/timeline2Win32.iss
--- a/tools/winbuildtools/inno/timeline2Win32.iss Sun Jul 06 08:20:45 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-; Script generated by the Inno Setup Script Wizard.
-; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
-
-[Setup]
-AppName=Timeline
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-;
-; Before running this script ...
-; The two lines below must be uncommented and text changed to reflect
-; the version number of the executable to be built.
-;
-AppVerName=Timeline 2.1.0
-OutputBaseFilename=timeline-2.1.0-beta-4b501487562b-2019-11-26-Win32Setup
-;
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-AppPublisher=Rickard Lindberg <ricli85@gmail.com>
-AppPublisherURL=http://thetimelineproj.sourceforge.net/
-AppSupportURL=http://thetimelineproj.sourceforge.net/
-AppUpdatesURL=http://thetimelineproj.sourceforge.net/
-DefaultDirName={pf}\Timeline
-DefaultGroupName=Timeline
-SourceDir=.\
-LicenseFile=..\..\..\COPYING
-InfoBeforeFile=WINSTALL
-OutputDir=out
-Compression=lzma
-SolidCompression=yes
-DisableDirPage=no
-
-[Languages]
-Name: "english"; MessagesFile: "compiler:Default.isl"
-
-
-[Tasks]
-Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
-Name: "startmenu"; Description: "Create a start menu"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
-
-[Files]
-Source: "..\dist\timeline.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
-Source: "..\dist\icons\*"; DestDir: "{app}\icons"; Flags: ignoreversion recursesubdirs
-Source: "..\dist\translations\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs
-
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-;
-; Before running this script ...
-; You must check to see if there are any more po-files to add
-;
-;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-
-[Icons]
-Name: "{commondesktop}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; Tasks: desktopicon
-Name: "{group}\Timeline"; Filename:"{app}\timeline.exe"; IconFilename: "{app}\icons\Timeline.ico"; WorkingDir: "{app}"; Tasks: startmenu
-
-
-
-
-[Run]
-Filename: "{app}\timeline.exe"; Description: "{cm:LaunchProgram,Timeline}"; Flags: shellexec postinstall skipifsilent;
-
-
-[UninstallDelete]
-Type: files; Name: "{win}\uninstall\MYPROG.INI"
-
-
changeset: 7988:c6f07716d723
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sun Jul 06 08:20:45 2025 +0200
summary: Remove no longer used winbuild tools.
diff -r 2d4564f72ba5 -r c6f07716d723 tools/build_win32_installer.py
--- a/tools/build_win32_installer.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-Working directory = BUILD_DIR
-COPYDIR Copies from TIMELINE_DIR to BUILD_DIR
-"""
-
-import argparse
-import sys
-import os
-import shutil
-import tempfile
-import subprocess
-import timelinetools.packaging.repository
-
-
-TIMELINE_DIR = os.path.abspath("..\\..\\")
-BUILD_DIR = os.path.abspath(".\\target")
-ARCHIVE = "archive"
-ARTIFACT = "artifact"
-
-COPYFILE = 0
-COPYDIR = 1
-PUSHD = 3
-POPD = 4
-RUNCMD = 5
-RUNPYSCRIPT = 6
-ANNOTATE = 8
-RUNPYTEST = 9
-
-ACTION_NAMES = {COPYFILE: "COPYFILE",
- COPYDIR: "COPYDIR",
- PUSHD: "PUSHD",
- POPD: "POPD",
- RUNCMD: "RUNCMD",
- RUNPYSCRIPT: "RUNPYSCRIPT",
- RUNPYTEST: "RUNPYTEST"
- }
-
-
-known_targets = ("win32Installer")
-
-
-win32InstallerActions = (
- (ANNOTATE, "Run Tests", ""),
- (RUNPYTEST, ["tools", "execute-specs.py"], ""),
-
- (ANNOTATE, "Modify source files", ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_paths_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_version_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_factory_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_iss_win32.py"], ""),
-
- (ANNOTATE, "Create a target directory for the build", ""),
- (COPYDIR, ["source", "timelinelib"], ["builddir", "timelinelib"]),
- (COPYDIR, ["tools", "winbuildtools", "inno"], ["builddir", "inno"]),
- (COPYFILE, ["source", "timeline.py"], ["builddir", "timeline.py"]),
- (COPYFILE, ["tools", "winbuildtools", "setup.py"], ["builddir", "setup.py"]),
- (COPYFILE, ["COPYING"], ["builddir", "COPYING"]),
- (COPYFILE, ["tools", "winbuildtools", "inno", "WINSTALL"], ["builddir", "WINSTALL"]),
-
- (ANNOTATE, "Create distribution directory", ""),
- (COPYDIR, ["icons"], ["builddir", "icons"]),
- (RUNPYSCRIPT, ["builddir", "setup.py"], "py2exe"),
- (COPYDIR, ["translations"], ["builddir", "dist", "translations"]),
- (COPYDIR, ["icons"], ["builddir", "dist", "icons"]),
- (COPYDIR, ["tools"], ["builddir", "dist", "tools"]),
-
- (ANNOTATE, "Create installer executable", ""),
- (RUNCMD, "python", ["builddir", "dist", "tools", "generate-mo-files.py"]),
-
- (ANNOTATE, "Create Setup executable", ""),
- (RUNCMD, "iscc.exe", ["builddir", "inno", "timelineWin32.iss"]),
-
- (ANNOTATE, "Deliver executable artifact", ""),
- (COPYFILE, [ARTIFACT], [ARTIFACT]),
-
- (ANNOTATE, "Done", ""),
-)
-
-
-actions = {"win32Installer": win32InstallerActions}
-
-
-class Target():
-
- def __init__(self, target):
- print("-------------------------------------------------------")
- print(" %s" % ("Building target %s" % target))
- print("-------------------------------------------------------")
- self.target = target
- self.actions = actions[target]
- self.ACTION_METHODS = {COPYFILE: self.copyfile,
- COPYDIR: self.copydir,
- PUSHD: self.pushd,
- POPD: self.popd,
- RUNCMD: self.runcmd,
- RUNPYSCRIPT: self.runpyscript,
- RUNPYTEST: self.runpytest,
- ANNOTATE: self.annotate}
-
- def build(self, arguments, artifact_dir):
- temp_dir = tempfile.mkdtemp()
- try:
- self.assert_that_target_is_known()
- self.setup_and_create_directories(arguments, artifact_dir, temp_dir)
- self.execute_actions()
- finally:
- #shutil.rmtree(temp_dir)
- pass
-
- def assert_that_target_is_known(self):
- if self.target not in known_targets:
- print("The target %s is unknown" % self.target)
- print("BUILD FAILED")
- sys.exit(1)
-
- def setup_and_create_directories(self, arguments, artifact_dir, temp_dir):
- self.artifact_dir = artifact_dir
- self.project_dir = self.create_project_directory(arguments, temp_dir)
- print("Artifact dir: %s" % self.artifact_dir)
- print("Project dir: %s" % self.project_dir)
- print("Working dir: %s" % os.getcwd())
-
- def create_project_directory(self, arguments, temp_dir):
- print("Create project directory")
- repository = timelinetools.packaging.repository.Repository()
- self.archive = repository.archive(arguments.revision, temp_dir, ARCHIVE)
- return os.path.join(temp_dir, ARCHIVE)
-
- def execute_actions(self):
- count = 0
- total = len([actions for action in self.actions if action[0] is not ANNOTATE])
- try:
- for action, src, dst in self.actions:
- if action is not ANNOTATE:
- count += 1
- print("Action %2d(%2d): %s" % (count, total, ACTION_NAMES[action]))
- self.ACTION_METHODS[action](src, dst)
- print("BUILD DONE")
- except Exception as ex:
- print(str(ex))
- print("BUILD FAILED")
- sys.exit(1)
-
- def annotate(self, src, dst):
- self.print_header(src)
-
- def copyfile(self, src, dst):
- if src[0] == ARTIFACT:
- f = os.path.join(self.project_dir, self.get_artifact_src_name())
- t = os.path.join(self.artifact_dir, self.get_artifact_target_name())
- else:
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copyfile(f, t)
-
- def copydir(self, src, dst):
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copytree(f, t)
-
- def runpyscript(self, src, arg):
- try:
- script_path = os.path.join(self.project_dir, *src)
- self.print_src_dst(script_path, arg)
- if src[-1] == "setup.py":
- self.pushd(os.path.join(self.project_dir, "builddir"), None)
- success, msg = self.run_pyscript(script_path, [arg])
- self.popd(None, None)
- else:
- success, msg = self.run_pyscript(script_path, [self.project_dir, arg])
- if not success:
- raise Exception(msg)
- except Exception as ex:
- pass
-
- def runpytest(self, src, dst):
- script_path = os.path.join(self.project_dir, *src)
- self.pushd(os.path.dirname(script_path), None)
- self.print_src_dst(src, os.path.abspath(dst))
- success, msg = self.run_pyscript(script_path, [], display_stderr=True)
- if not success:
- print('Msg:', msg)
- if msg:
- raise Exception(msg)
- self.popd(None, None)
-
- def runcmd(self, src, dst):
- t = os.path.join(self.project_dir, *dst)
- self.pushd(os.path.dirname(t), None)
- self.print_src_dst(src, t)
- success, msg = self.run_command([src, t])
- self.popd(None, None)
- if not success:
- raise Exception(msg)
-
- def pushd(self, src, dst):
- self.print_src_dst(os.getcwd(), os.path.abspath(src))
- self.cwd = os.getcwd()
- os.chdir(src)
-
- def popd(self, src, dst):
- self.print_src_dst(None, self.cwd)
- print(" dst: %s" % self.cwd)
- os.chdir(self.cwd)
-
- def run_pyscript(self, script, args=[], display_stderr=False):
- return self.run_command(["python", script] + args, display_stderr)
-
- def run_command(self, cmd, display_stderr=False):
- if display_stderr:
- rc = subprocess.call(cmd)
- return rc == 0, ""
- else:
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out = p.communicate()
- print(out)
- if p.returncode == 0:
- return True, out[0]
- else:
- return False, out[1]
-
- def print_header(self, message):
- print("-------------------------------------------------------")
- print(" %s" % message)
- print("-------------------------------------------------------")
-
- def print_src_dst(self, src, dst):
- if src is not None:
- print(" src: %s" % src)
- if dst is not None:
- print(" dst: %s" % dst)
-
- def get_artifact_src_name(self):
- versionfile = os.path.join(self.project_dir, "source", "timelinelib", "meta", "version.py")
- f = open(versionfile, "r")
- text = f.read()
- lines = text.split("\n")
- for line in lines:
- if line[0:7] == "VERSION":
- break
- f.close()
- # VERSION = (0, 14, 0)
- line = line.split("(", 1)[1]
- line = line.split(")", 1)[0]
- major, minor, bug = line. split(", ")
- return "SetupTimeline%s%s%sPy2ExeWin32.exe" % (major, minor, bug)
-
- def get_artifact_target_name(self):
- return "%s-Win32Setup.exe" % self.archive.get_filename_version()
-
-
-def main():
- artifactdir = os.path.join(sys.path[0], "..")
- Target("win32Installer").build(parse_arguments(), artifactdir)
-
-
-def parse_arguments():
- parser = argparse.ArgumentParser()
- parser.add_argument("--revision", default="tip")
- return parser.parse_args()
-
-
-if __name__ == "__main__":
- main()
diff -r 2d4564f72ba5 -r c6f07716d723 tools/build_win32_installer_py27.py
--- a/tools/build_win32_installer_py27.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-Working directory = BUILD_DIR
-COPYDIR Copies from TIMELINE_DIR to BUILD_DIR
-"""
-
-import argparse
-import sys
-import os
-import shutil
-import tempfile
-import subprocess
-import timelinetools.packaging.repository
-
-
-TIMELINE_DIR = os.path.abspath("..\\..\\")
-BUILD_DIR = os.path.abspath(".\\target")
-ARCHIVE = "archive"
-ARTIFACT = "artifact"
-
-COPYFILE = 0
-COPYDIR = 1
-PUSHD = 3
-POPD = 4
-RUNCMD = 5
-RUNPYSCRIPT = 6
-ANNOTATE = 8
-RUNPYTEST = 9
-
-ACTION_NAMES = {COPYFILE: "COPYFILE",
- COPYDIR: "COPYDIR",
- PUSHD: "PUSHD",
- POPD: "POPD",
- RUNCMD: "RUNCMD",
- RUNPYSCRIPT: "RUNPYSCRIPT",
- RUNPYTEST: "RUNPYTEST"
- }
-
-
-known_targets = ("win32Installer")
-
-
-win32InstallerActions = (
- (ANNOTATE, "Run Tests", ""),
- (RUNPYTEST, ["tools", "execute-specs.py"], ""),
-
- (ANNOTATE, "Modify source files", ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_paths_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_version_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_factory_py.py"], ""),
- (RUNPYSCRIPT, ["tools", "winbuildtools", "mod_timeline_iss_win32_py27.py"], ""),
-
- (ANNOTATE, "Create a target directory for the build", ""),
- (COPYDIR, ["source", "timelinelib"], ["builddir", "timelinelib"]),
- (COPYDIR, ["dependencies", "timelinelib", "icalendar-3.2\icalendar"], ["builddir", "icalendar"]),
- (COPYDIR, ["dependencies", "timelinelib", "pytz-2012j\pytz"], ["builddir", "pytz"]),
- (COPYDIR, ["dependencies", "timelinelib", "markdown-2.0.3", "markdown"], ["builddir", "markdown"]),
- (COPYDIR, ["tools", "winbuildtools", "inno"], ["builddir", "inno"]),
- (COPYFILE, ["source", "timeline.py"], ["builddir", "timeline.py"]),
- (COPYFILE, ["tools", "winbuildtools", "setup.py"], ["builddir", "setup.py"]),
- (COPYFILE, ["COPYING"], ["builddir", "COPYING"]),
- (COPYFILE, ["tools", "winbuildtools", "inno", "WINSTALL"], ["builddir", "WINSTALL"]),
-
- (ANNOTATE, "Create distribution directory", ""),
- (COPYDIR, ["icons"], ["builddir", "icons"]),
- (RUNPYSCRIPT, ["builddir", "setup.py"], "py2exe"),
- (COPYDIR, ["translations"], ["builddir", "dist", "translations"]),
- (COPYDIR, ["icons"], ["builddir", "dist", "icons"]),
- (COPYDIR, ["tools"], ["builddir", "dist", "tools"]),
-
- (ANNOTATE, "Create installer executable", ""),
- (RUNCMD, "python", ["builddir", "dist", "tools", "generate-mo-files.py"]),
-
- (ANNOTATE, "Create Setup executable", ""),
- (RUNCMD, "iscc.exe", ["builddir", "inno", "timelineWin32Py27.iss"]),
-
- (ANNOTATE, "Deliver executable artifact", ""),
- (COPYFILE, [ARTIFACT], [ARTIFACT]),
-
- (ANNOTATE, "Done", ""),
- )
-
-
-actions = {"win32Installer": win32InstallerActions}
-
-
-class Target():
-
- def __init__(self, target):
- print("-------------------------------------------------------")
- print(" %s" % ("Building target %s" % target))
- print("-------------------------------------------------------")
- self.target = target
- self.actions = actions[target]
- self.ACTION_METHODS = {COPYFILE: self.copyfile,
- COPYDIR: self.copydir,
- PUSHD: self.pushd,
- POPD: self.popd,
- RUNCMD: self.runcmd,
- RUNPYSCRIPT: self.runpyscript,
- RUNPYTEST: self.runpytest,
- ANNOTATE: self.annotate}
-
- def build(self, arguments, artifact_dir):
- temp_dir = tempfile.mkdtemp()
- try:
- self.assert_that_target_is_known()
- self.setup_and_create_directories(arguments, artifact_dir, temp_dir)
- self.execute_actions()
- finally:
- # shutil.rmtree(temp_dir)
- pass
-
- def assert_that_target_is_known(self):
- if self.target not in known_targets:
- print("The target %s is unknown" % self.target)
- print("BUILD FAILED")
- sys.exit(1)
-
- def setup_and_create_directories(self, arguments, artifact_dir, temp_dir):
- self.artifact_dir = artifact_dir
- self.project_dir = self.create_project_directory(arguments, temp_dir)
- print("Artifact dir: %s" % self.artifact_dir)
- print("Project dir: %s" % self.project_dir)
- print("Working dir: %s" % os.getcwd())
-
- def create_project_directory(self, arguments, temp_dir):
- print("Create project directory")
- repository = timelinetools.packaging.repository.Repository()
- self.archive = repository.archive(arguments.revision, temp_dir, ARCHIVE)
- return os.path.join(temp_dir, ARCHIVE)
-
- def execute_actions(self):
- count = 0
- total = len([actions for action in self.actions if action[0] is not ANNOTATE])
- try:
- for action, src, dst in self.actions:
- if action is not ANNOTATE:
- count += 1
- print("Action %2d(%2d): %s" % (count, total, ACTION_NAMES[action]))
- self.ACTION_METHODS[action](src, dst)
- print("BUILD DONE")
- except Exception as ex:
- print(str(ex))
- print("BUILD FAILED")
- sys.exit(1)
-
- def annotate(self, src, dst):
- self.print_header(src)
-
- def copyfile(self, src, dst):
- if src[0] == ARTIFACT:
- f = os.path.join(self.project_dir, self.get_artifact_src_name())
- t = os.path.join(self.artifact_dir, self.get_artifact_target_name())
- else:
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copyfile(f, t)
-
- def copydir(self, src, dst):
- f = os.path.join(self.project_dir, *src)
- t = os.path.join(self.project_dir, *dst)
- self.print_src_dst(f, t)
- shutil.copytree(f, t)
-
- def runpyscript(self, src, arg):
- try:
- script_path = os.path.join(self.project_dir, *src)
- self.print_src_dst(script_path, arg)
- if src[-1] == "setup.py":
- self.pushd(os.path.join(self.project_dir, "builddir"), None)
- success, msg = self.run_pyscript(script_path, [arg])
- self.popd(None, None)
- else:
- success, msg = self.run_pyscript(script_path, [self.project_dir, arg])
- if not success:
- raise Exception(msg)
- except Exception:
- pass
-
- def runpytest(self, src, dst):
- script_path = os.path.join(self.project_dir, *src)
- self.pushd(os.path.dirname(script_path), None)
- self.print_src_dst(src, os.path.abspath(dst))
- success, msg = self.run_pyscript(script_path, [], display_stderr=True)
- if not success:
- raise Exception(msg)
- self.popd(None, None)
-
- def runcmd(self, src, dst):
- t = os.path.join(self.project_dir, *dst)
- self.pushd(os.path.dirname(t), None)
- self.print_src_dst(src, t)
- success, msg = self.run_command([src, t])
- self.popd(None, None)
- if not success:
- raise Exception(msg)
-
- def pushd(self, src, dst):
- self.print_src_dst(os.getcwd(), os.path.abspath(src))
- self.cwd = os.getcwd()
- os.chdir(src)
-
- def popd(self, src, dst):
- self.print_src_dst(None, self.cwd)
- print(" dst: %s" % self.cwd)
- os.chdir(self.cwd)
-
- def run_pyscript(self, script, args=[], display_stderr=False):
- return self.run_command(["python", script] + args, display_stderr)
-
- def run_command(self, cmd, display_stderr=False):
- print('cmd:', cmd)
- if display_stderr:
- rc = subprocess.call(cmd)
- return rc == 0, ""
- else:
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out = p.communicate()
- print(out)
- if p.returncode == 0:
- return True, out[0]
- else:
- return False, out[1]
-
- def print_header(self, message):
- print("-------------------------------------------------------")
- print(" %s" % message)
- print("-------------------------------------------------------")
-
- def print_src_dst(self, src, dst):
- if src is not None:
- print(" src: %s" % src)
- if dst is not None:
- print(" dst: %s" % dst)
-
- def get_artifact_src_name(self):
- versionfile = os.path.join(self.project_dir, "source", "timelinelib", "meta", "version.py")
- f = open(versionfile, "r")
- text = f.read()
- lines = text.split("\n")
- for line in lines:
- if line[0:7] == "VERSION":
- break
- f.close()
- # VERSION = (0, 14, 0)
- line = line.split("(", 1)[1]
- line = line.split(")", 1)[0]
- major, minor, bug = line. split(", ")
- return "SetupTimeline%s%s%sPy2ExeWin32.exe" % (major, minor, bug)
-
- def get_artifact_target_name(self):
- return "%s-Win32Setup.exe" % self.archive.get_filename_version()
-
-def main():
- artifactdir = os.path.join(sys.path[0], "..")
- Target("win32Installer").build(parse_arguments(), artifactdir)
-
-
-def parse_arguments():
- parser = argparse.ArgumentParser()
- parser.add_argument("--revision", default="tip")
- return parser.parse_args()
-
-
-if __name__ == "__main__":
- main()
diff -r 2d4564f72ba5 -r c6f07716d723 tools/winbuildtools/build_win_setup_executable.py
--- a/tools/winbuildtools/build_win_setup_executable.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,374 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-This script is used to create a Timeline installation file for Windows
-
-Usage:
- Copy this file to a temporary directory and issue the command
- python build_win_setup_executable.py [version]
-
- If a version argument is given, it must be the name of a tagged version
- such as 2.10.0.
- If not given, the latest code is used.
-
- The path to the python version used is defined by the pragram constant
- PYTHON_HOME. If default isn't what you want you have to change it.
-
-Prerequisite:
- 1. Must run on a Windows machine
- 2. Mercurial (hg) must be installed
- 3. Python must be installed
- 4. Inno Setup (iscc.exe) must be installed
- 5. Dependent on files in Timeline repo:
- tools/execute-specs.py
- tools/generate-mo-files.py
- tools/winbuildtools/mod_paths.py
- tools/winbuildtools/mod_iss_timeline_version.py
- tools/winbuildtools/timeline.spec
- tools/winbuildtools/dist/icons/*
- tools/winbuildtools/dist/translations/*
-
-
-"""
-
-import os
-import subprocess
-from cmdlineparser import CmdLineParser, PositionalArg, NamedArg
-
-
-class BuildError(Exception):
- pass
-
-
-class Build:
-
- def __init__(self, python_home, build_version=None, keep_workspace=False):
- self.PYTHON = os.path.join(python_home, "python.exe")
- self.PIP = os.path.join(python_home, "pip.exe")
- self.PYINSTALLER = None
- self._build_version = build_version
- self._root_dir = os.path.dirname(os.path.realpath(__file__))
- self._keep_workspace = keep_workspace
- self._log_header("Input data")
- self._log(f"Python home : {python_home}")
- self._log(f"Build version : {self._build_version}")
- self._log(f"Keep workspace: {self._keep_workspace}")
-
- def _validate_prerequisite(self):
- def os_is_windows():
- if os.name == "nt":
- self._log("OS is windows")
- else:
- raise BuildError("This script only runs on Windows!")
-
- def mercurial_is_installed():
- if self._os_command("where", "hg").endswith("hg.exe"):
- self._log("Mercurial is installed")
- else:
- raise BuildError("Mercurial is not installed!")
-
- def python_installed():
- try:
- version = self._os_command(self.PYTHON, "-V")
- self._log(f"Python {version} is installed")
- except:
- raise BuildError("Python is not installed!")
-
- def iscc_is_installed():
- if self._os_command("where", "iscc").lower().endswith("iscc.exe"):
- self._log("ISCC is installed")
- else:
- raise BuildError("ISCC is not installed!")
-
- self._log_header("Validating Prerequisite")
- os_is_windows()
- mercurial_is_installed()
- python_installed()
- iscc_is_installed()
-
- def _create_workspace(self):
- self._create_dir("workspace")
- self._cd("workspace")
-
- def _os_command(self, *args):
- return subprocess.check_output(list(args)).decode("latin-1").strip()
-
- def _create_dir(self, dirname):
- os.mkdir(dirname)
-
- def _cd(self, dirname):
- if dirname == "root":
- path = self._root_dir
- else:
- path = os.path.join(self._root_dir, dirname)
- os.chdir(path)
- self._log(f"Directory changed to: {os.getcwd()}")
-
- def _remove_dir(self, dirname):
- os.rmdir(dirname)
-
- def _get_timeline_from_repo(self):
- """http://hg.code.sf.net/p/thetimelineproj/main"""
- self._log_header("Clone Timeline repo")
- os.system("hg clone http://hg.code.sf.net/p/thetimelineproj/main .")
- self._log_header("Get hg info")
- self._hg_node = self._get_hg_node()
- self._build_version_exists()
- self._hg_rev = self._get_hg_rev()
- self._log(f"Build version: {self._build_version}")
- self._log(f"Node : {self._hg_node}")
- self._log(f"Revision : {self._hg_rev}")
-
- def _build_version_exists(self):
- if self._build_version is not None:
- try:
- self._os_command(
- "hg", "log", "--rev", self._build_version, "--template", "exists"
- )
- self._log("Build version exists")
- self._os_command("hg", "update", "--rev", self._build_version)
- self._log("Project updated to build version")
- except:
- raise BuildError(f"unknown revision: {self._build_version}!")
-
- def _get_hg_node(self):
- return self._os_command("hg", "log", "--rev", ".", "--template", "{node}")
-
- def _get_hg_rev(self):
- return self._os_command("hg", "log", "--rev", ".", "--template", "{rev}")
-
- def _pip_install(self, package, version=None):
- self._log(f'Install {package} {version if version is not None else ""}')
- if version is not None:
- self._os_command(self.PIP, "install", "-v", f"{package}=={version}")
- else:
- self._os_command(self.PIP, "install", package)
-
- def _create_virtual_environment(self):
- def create_venv():
- self._os_command(self.PYTHON, "-m", "venv", "venv")
- self.PYTHON = os.path.join(
- self._root_dir, "workspace", "venv", "Scripts", "python.exe"
- )
- self.PIP = os.path.join(
- self._root_dir, "workspace", "venv", "Scripts", "pip.exe"
- )
-
- def upgrade_pip():
- self._log("Upgrade pip")
- self._os_command(self.PYTHON, "-m", "pip", "install", "--upgrade", "pip")
-
- def install_packages():
- packages = (
- ("humblewx", None),
- ("icalendar", None),
- ("Markdown", None),
- ("pyinstaller", None),
- ("wxPython", "4.1.1"),
- )
- for package, version in packages:
- self._pip_install(package, version)
- self.PYINSTALLER = os.path.join(
- self._root_dir, "workspace", "venv", "Scripts", "pyinstaller.exe"
- )
- self._log(self._os_command(self.PIP, "list"))
-
- self._log_header("Create virtual environment")
- create_venv()
- upgrade_pip()
- install_packages()
-
- def _run_tests(self):
- self._log_header("Run Tests")
- self._os_command(
- self.PYTHON, "tools/execute-specs.py", "--write-testlist", "testlist.txt"
- )
-
- def _generate_mo_file(self):
- self._log_header("Generate mo files")
- try:
- self._cd(os.path.join(self._root_dir, "workspace", "tools"))
- self._os_command(self.PYTHON, "generate-mo-files.py")
- finally:
- self._cd(os.path.join(self._root_dir, "workspace"))
-
- def _modify_source_files(self):
- self._log_header("Modify source files")
- self._cd(os.path.join(self._root_dir, "workspace", "tools", "winbuildtools"))
- self._os_command(self.PYTHON, "mod_paths.py", ".")
- self._log("paths.py modified")
- self._log(f"Revision: {self._build_version or self._hg_node}")
- self._os_command(
- self.PYTHON,
- "mod_iss_timeline_version.py",
- ".",
- self._build_version or self._hg_node,
- )
- self._log("version.py modified")
-
- def _create_timeline_executable(self):
- self._log_header("Create Timeline executable")
- self._cd(os.path.join(self._root_dir, "workspace", "tools", "winbuildtools"))
- self._os_command(self.PYINSTALLER, "timeline.spec")
-
- def _copy_files_to_dist(self):
- self._log("Copying icons and translations to dist")
- self._cd(os.path.join(self._root_dir, "workspace", "tools", "winbuildtools"))
- self._create_dir(".\\dist\\icons")
- self._create_dir(".\\dist\\icons\\event_icons")
- self._create_dir(".\\dist\\translations")
-
- self._os_command("xcopy", "/S", "..\\..\\icons\*.*", ".\\dist\icons\\*.*")
- self._log(f"Icons copied")
-
- for lang in (
- "ca",
- "de",
- "el",
- "es",
- "eu",
- "fr",
- "gl",
- "he",
- "it",
- "ko",
- "lt",
- "nl",
- "pl",
- "pt",
- "pt_BR",
- "ru",
- "sv",
- "tr",
- "vi",
- "zh_CN",
- ):
- self._os_command(
- "xcopy",
- "/S",
- f"..\\..\\translations\\{lang}\\*.*",
- f".\\dist\\translations\\{lang}\\*.*",
- )
- self._log(f"{lang} translations copied")
-
- def _create_distrubtable(self):
- self._log_header("Create distributable")
- self._copy_files_to_dist()
- self._cd(
- os.path.join(self._root_dir, "workspace", "tools", "winbuildtools", "inno")
- )
- self._os_command("iscc.exe", "timeline2Win32.iss")
-
- def _move_result(self):
- self._log_header("Move result")
- self._cd(
- os.path.join(
- self._root_dir, "workspace", "tools", "winbuildtools", "inno", "out"
- )
- )
- self._os_command("xcopy", "/Y", "*.exe", f"{self._root_dir}\\*.*")
- self._log("Resulting exe-file moved to root dir.")
-
- def _log_header(self, text):
- print("-" * 50)
- print(f" {text}")
- print("-" * 50)
-
- def _log(self, text):
- print(f" {text}")
-
- def _abort_message(self, ex):
- self._log_header("Build aborted:")
- self._log(ex)
-
- def _clean_up(self):
- self._log_header("Clean up")
- self._cd("root")
- if os.path.isdir("workspace"):
- os.system("rmdir /S /Q workspace")
- self._log("workspace directory removed")
- else:
- self._log("No workspace found")
- if os.path.isdir("__pycache__"):
- os.system("rmdir /S /Q __pycache__")
- self._log("__pycache__ directory removed")
-
- def run(self):
- try:
- self._clean_up()
- self._validate_prerequisite()
- self._create_workspace()
- self._get_timeline_from_repo()
- self._create_virtual_environment()
- self._run_tests()
- self._generate_mo_file()
- self._modify_source_files()
- self._create_timeline_executable()
- self._create_distrubtable()
- self._move_result()
- except BuildError as ex:
- self._abort_message(ex)
- finally:
- if not self._keep_workspace:
- self._clean_up()
-
-
-def print_help():
- print(
- """
-Usage:
-
- python build_win_setup_executable.py [options]
-
-Options:
-
- -h, --help Print help
- -p dirname, --python-home dirname Path to dir where python.exe can be found. Default = c:\pgm\python396
- -t tagname, --tag tagname Tagged version to build. Default = latest code
- -k, --keep-workspace Kepp the workspace directory when finished
-
- """
- )
-
-
-if __name__ == "__main__":
- DEFAULT_PYTHON_HOME = "c:\\pgm\\Python396"
- specs = [
- NamedArg(name="t", long_name="tag", has_value=True),
- NamedArg(name="k", long_name="keep-workspace"),
- NamedArg(name="p", long_name="python-home", has_value=True),
- NamedArg(name="h", long_name="help"),
- ]
- parser = CmdLineParser(specs)
- if parser.help:
- print_help()
- else:
- build_version = parser.tag_value
- python_home = (
- parser.python_home_value
- if parser.python_home_value
- else DEFAULT_PYTHON_HOME
- )
- Build(
- python_home=python_home,
- build_version=build_version,
- keep_workspace=parser.keep_workspace,
- ).run()
diff -r 2d4564f72ba5 -r c6f07716d723 tools/winbuildtools/buildwinsetup.cmd
--- a/tools/winbuildtools/buildwinsetup.cmd Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-@echo off
-
-echo --------------------------------------
-echo Revision: %1
-echo --------------------------------------
-
-
-echo --------------------------------------
-echo Remove old build and dist directories
-echo --------------------------------------
-rmdir /S /Q build >> nul
-rmdir /S /Q dist >> nul
-del /S /Q inno\out >> nul
-
-echo --------------------------------------
-echo Create translations
-echo --------------------------------------
-pushd ..
-python generate-mo-files.py
-popd
-
-echo --------------------------------------
-echo Modify paths.py
-echo --------------------------------------
-python mod_paths.py .
-
-echo --------------------------------------
-echo Modify version file and iss file
-echo --------------------------------------
-python mod_iss_timeline_version.py . %1
-
-echo --------------------------------------
-echo Building distribution
-echo --------------------------------------
-pyinstaller timeline.spec
-
-echo --------------------------------------
-echo Copying icons and translations to dist
-echo --------------------------------------
-mkdir .\dist\icons\event_icons
-mkdir .\dist\translations
-xcopy /S ..\..\icons\*.* .\dist\icons\*.*
-xcopy /S ..\..\translations\ca\*.* .\dist\translations\ca\*.*
-xcopy /S ..\..\translations\de\*.* .\dist\translations\de\*.*
-xcopy /S ..\..\translations\el\*.* .\dist\translations\el\*.*
-xcopy /S ..\..\translations\es\*.* .\dist\translations\es\*.*
-xcopy /S ..\..\translations\eu\*.* .\dist\translations\eu\*.*
-xcopy /S ..\..\translations\fr\*.* .\dist\translations\fr\*.*
-xcopy /S ..\..\translations\gl\*.* .\dist\translations\gl\*.*
-xcopy /S ..\..\translations\he\*.* .\dist\translations\he\*.*
-xcopy /S ..\..\translations\it\*.* .\dist\translations\it\*.*
-xcopy /S ..\..\translations\ko\*.* .\dist\translations\ko\*.*
-xcopy /S ..\..\translations\lt\*.* .\dist\translations\lt\*.*
-xcopy /S ..\..\translations\nl\*.* .\dist\translations\nl\*.*
-xcopy /S ..\..\translations\pl\*.* .\dist\translations\pl\*.*
-xcopy /S ..\..\translations\pt\*.* .\dist\translations\pt\*.*
-xcopy /S ..\..\translations\pt_BR\*.* .\dist\translations\pt_BR\*.*
-xcopy /S ..\..\translations\ru\*.* .\dist\translations\ru\*.*
-xcopy /S ..\..\translations\sv\*.* .\dist\translations\sv\*.*
-xcopy /S ..\..\translations\tr\*.* .\dist\translations\tr\*.*
-xcopy /S ..\..\translations\vi\*.* .\dist\translations\vi\*.*
-xcopy /S ..\..\translations\zh_CH\*.* .\dist\translations\zh_CH\*.*
-
-
-echo --------------------------------------
-echo Create distributable
-echo --------------------------------------
-pushd inno
-iscc.exe timeline2Win32.iss
-popd
diff -r 2d4564f72ba5 -r c6f07716d723 tools/winbuildtools/cmdlineparser.py
--- a/tools/winbuildtools/cmdlineparser.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-The CmdLineParser reads the command line input and stores the separate
-parts as properties, according to the given specification.
-
-A command line has two types of arguments
- - Positional argument
- - Named argument
-
-Arguments are separated with on or more spaces.
-
-A named argument is always prefixed with a dash (-) which a named argument isn't.
-
-A named argument has two types. One with a single dash prefix and one with a double
-dash prefix. The first one is intended for single character names and the second
-one for long names. Both can have a specified value.
-
-If a name contains a dash character, the attribute name of the parser will be the same
-but with the dash replaced with an underscore character.
-
-The name 'invalid-args' is reserved for the parser and cannot be used as a command line
-argument.
-
-By default, the input argument list is taken from sys.argv[1:], but it can be
-specified as an argument to the CmdLineParser constructor.
-
-Example:
- cmd line: pgm.exe -v --log-name mylog filename
-
- pgm.exe The name of the executed program
- -v A named argument without a value
- --log-name mylog A named argument with the value mylog
- filename A positional argument
-
-Usage:
- # Define valid arguments
- specs = [
- PositionalArg(name="filename"),
- NamedArg(name="v", long_name="verbose"),
- NamedArg(long_name="log-name", has_value=True),
- ]
-
- # Create the parser and process the command line input
- parser = CmdLineParser(specs)
-
- # Use input data
- if parser.verbose:
- print("Verbose mode")
- print(f"logfile : {parser.log_name}")
- print(f"filename: {parser.filename}")
-
-"""
-
-import sys
-
-
-class Arg:
- def __init__(self, name="", long_name="", has_value=False):
- self._name = name
- self._long_name = long_name
- self._has_value = has_value
-
- @property
- def name(self):
- return self._name
-
- @property
- def long_name(self):
- return self._long_name
-
- @property
- def has_value(self):
- return self._has_value
-
-
-class PositionalArg(Arg):
- def __init__(self, name):
- Arg.__init__(self, name)
-
-
-class NamedArg(Arg):
- def __init__(self, name="", long_name="", has_value=None):
- Arg.__init__(self, name, long_name, has_value)
-
-
-class CmdLineParser:
- """
- No arguments given on the command line
-
- >>> sys.argv = ["pgm.exe"]
- >>> specs = [
- ... PositionalArg(name="filename"),
- ... NamedArg(name="v", long_name="verbose"),
- ... NamedArg(name="l", long_name="log-name", has_value=True),]
- >>> parser = CmdLineParser(specs)
- >>> parser.filename
- False
- >>> parser.log_name
- False
- >>> parser.l
- False
- >>> parser.verbose
- False
- >>> parser.v
- False
- >>> parser.invalid_args
- []
-
- Command line arguments given
-
- >>> sys.argv = ["pgm.exe", "-v", "--log-name", "mylog", "myfile"]
- >>> parser = CmdLineParser(specs)
- >>> parser.filename
- 'myfile'
- >>> parser.v and parser.verbose
- True
- >>> parser.log_name_value
- 'mylog'
- >>> parser.l_value
- 'mylog'
-
- Invalid Command line arguments given
-
- >>> sys.argv = ["pgm.exe", "-vv", "--log-name", "mylog", "myfile"]
- >>> parser = CmdLineParser(specs)
- >>> len(parser.invalid_args)
- 1
- >>> parser.invalid_args[0]
- '-vv'
-
- Same arg given twice
-
- >>> sys.argv = ["pgm.exe", "--log-name", "mylog2", "--log-name", "mylog2", "myfile1", "myfile2"]
- >>> parser = CmdLineParser(specs)
-
- If named args are duplicated the last one is used
-
- >>> parser.log_name_value
- 'mylog2'
-
- Positional arguments are always assigned from left to right.
-
- >>> parser.filename
- 'myfile1'
- >>> parser.invalid_args[0]
- 'myfile2'
-
- The 'name' agument is not mandatory for NamedArg
-
- >>> sys.argv = ["pgm.exe", "--verbose", "myfile1"]
- >>> specs = [
- ... PositionalArg(name="filename"),
- ... NamedArg(long_name="verbose")]
- >>> parser = CmdLineParser(specs)
- >>> parser.verbose
- True
- """
-
- def __init__(self, specs, args=None):
- self._positional_specs = [s for s in specs if isinstance(s, PositionalArg)]
- self._named_specs = [s for s in specs if isinstance(s, NamedArg)]
- self._args = args or sys.argv[1:]
- self._invalid_args = []
- self._positional_args = []
- self._init_args()
- self._parse()
-
- @property
- def invalid_args(self):
- return self._invalid_args
-
- def _init_args(self):
- for spec in self._positional_specs:
- spec.attr_name = self._attr_name(spec.name)
- setattr(self, spec.attr_name, False)
- for spec in self._named_specs:
- self._create_attribute(spec)
- self._set_attribute(spec, False)
- self._set_attribute_value(spec, None)
-
- def _create_attribute(self, spec):
- if len(spec.name) > 0:
- spec.attr_name = self._attr_name(spec.name)
- if len(spec.long_name) > 0:
- spec.attr_long_name = self._attr_name(spec.long_name)
-
- def _set_attribute(self, spec, value):
- if len(spec.name) > 0:
- setattr(self, spec.attr_name, value)
- if len(spec.long_name) > 0:
- setattr(self, spec.attr_long_name, value)
-
- def _set_attribute_value(self, spec, value):
- if len(spec.name) > 0:
- setattr(self, f"{spec.attr_name}_value", value)
- if len(spec.long_name) > 0:
- setattr(self, f"{spec.attr_long_name}_value", value)
-
- def _attr_name(self, name):
- return name.replace("-", "_")
-
- @property
- def _next_arg(self):
- return self._args[0]
-
- @property
- def _more_args_exists(self):
- return len(self._args) > 0
-
- @property
- def _next_arg_is_named(self):
- return self._next_arg.startswith("-")
-
- @property
- def _next_arg_is_positional(self):
- return not self._next_arg_is_named
-
- def _remove_first_arg(self):
- self._args = self._args[1:]
-
- def _parse(self):
- while self._more_args_exists:
- if self._next_arg_is_named:
- self._parse_named_arg()
- else:
- self._parse_positional_arg()
-
- def _parse_named_arg(self):
- spec = self._find_spec()
- if spec is None:
- self._invalid_args.append(self._next_arg)
- self._remove_first_arg()
- else:
- self._set_attribute(spec, True)
- self._remove_first_arg()
- if spec.has_value and self._more_args_exists:
- if self._next_arg_is_positional:
- self._set_attribute_value(spec, self._next_arg)
- self._remove_first_arg()
-
- def _find_spec(self):
- for spec in self._named_specs:
- if (
- f"-{spec.name}" == self._next_arg
- or f"--{spec.long_name}" == self._next_arg
- ):
- return spec
-
- def _parse_positional_arg(self):
- index = len(self._positional_args)
- try:
- spec = self._positional_specs[index]
- setattr(self, spec.attr_name, self._next_arg)
- self._positional_args.append(self._next_arg)
- except IndexError:
- self._invalid_args.append(self._next_arg)
- self._remove_first_arg()
-
-
-if __name__ == "__main__":
- import doctest
-
- doctest.testmod()
diff -r 2d4564f72ba5 -r c6f07716d723 tools/winbuildtools/jenkins-build-local.py
--- a/tools/winbuildtools/jenkins-build-local.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,306 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-"""
-Our Jenkins server is about to be retired.
-
-That means that the Windows installation executable can no longer
-be built from Jenkins (calling a build slave).
-
-This script is intended to be run on the local Windows box that
-has the Jenkins slave installation to build the Windows installation
-executable file.
-
-"""
-
-import sys
-import os
-import subprocess
-
-
-class BuildError(Exception):
- pass
-
-
-def get_build_version_from_input():
- """
- The first argument, if any, is the tagged version t obe built.
- For beta releases, the hg rev number is used.
-
- >>> get_build_version_from_input()
-
- >>> sys.argv.insert(1, '2.11.0')
- >>> get_build_version_from_input()
- '2.11.0'
-
- """
- try:
- return sys.argv[1]
- except IndexError:
- pass
-
-
-def is_windows():
- return os.name == 'nt'
-
-
-def hg_command(*args):
- """
- >>> hg_command('showconfig', 'paths.default')
- 'http://hg.code.sf.net/p/thetimelineproj/main'
-
- """
- try:
- if is_windows():
- return subprocess.check_output(['hg'] + list(args)).decode('latin-1').strip()
- else:
- return subprocess.check_output(['hg'] + list(args)).decode().strip()
- except Exception as ex:
- raise BuildError(str(ex))
-
-
-def os_command(*args):
- try:
- if is_windows():
- return subprocess.check_output(list(args)).decode('latin-1').strip()
- else:
- return subprocess.check_output(list(args)).decode().strip()
- except Exception as ex:
- raise BuildError(str(ex))
-
-
-def get_hg_default_path():
- return hg_command('showconfig', 'paths.default')
-
-
-def hg_pull():
- hg_command('pull', '--rev', 'default')
-
-
-def hg_update():
- hg_command('update', '--clean', '--rev', 'default')
-
-
-def hg_purge():
- hg_command('--config', 'extensions.purge=', 'clean', '--all')
-
-
-def get_hg_node():
- return hg_command('log', '--rev', '.', '--template', '{node}')
-
-
-def get_hg_rev():
- return hg_command('log', '--rev', '.', '--template', '{rev}')
-
-
-def verify_version(build_version, node):
- """
- >>> verify_version('2.10.0', 'a821cfd8cbe1f28ac4d01ffe02746632161db27b')
- 'exists'
-
- >>> verify_version(None, 'a821cfd8cbe1f28ac4d01ffe02746632161db27b')
- 'exists'
-
- >>> verify_version(None, 'abcd')
- Traceback (most recent call last):
- ...
- jenkins-build-local.BuildError: unknown revision: abcd!
- """
- try:
- return hg_command('log', '--rev', build_version or node, '--template', 'exists')
- except:
- raise BuildError(f'unknown revision: {build_version or node}!')
-
-
-def log_changesets(build_revision, node):
- return hg_command(
- 'log',
- '--template',
- "<changeset node='{node}' author='{author|xmlescape}' rev='{rev}' date='{date}'><msg>{desc|xmlescape}</msg>{file_adds % '<addedFile>{file|xmlescape}</addedFile>'}{file_dels % '<deletedFile>{file|xmlescape}</deletedFile>'}{files % '<file>{file|xmlescape}</file>'}<parents>{parents}</parents></changeset>\n",
- '--rev',
- f"ancestors('default') and not ancestors({build_revision or node})",
- '--encoding',
- 'UTF-8',
- '--encodingmode',
- 'replace')
-
-
-def upgrade_pip():
- os_command('python.exe', '-m', 'pip', 'install', '--upgrade', 'pip')
-
-
-def display_python_version():
- log_subheader('Python Version')
- log(os_command('python', '-V'))
-
-
-def create_virtual_environment():
- log_subheader('Crate virtual environment')
- log(os_command('python', '-m', 'venv', 'venv'))
-
-
-def upgrade_pip():
- log_subheader('Upgrade pip')
- os.system('venv\\Scripts\\python -m pip install --upgrade pip')
-
-
-def install_wxpython():
- log_subheader('Install wwPython')
- os_command('venv\\Scripts\\pip', 'install', '--force-reinstall', '-v', 'wxPython==4.1.1')
-
-
-def install_humblewx():
- log_subheader('Install humblewx')
- os_command('venv\Scripts\\pip', 'install', 'humblewx')
-
-
-def install_icalendar():
- log_subheader('Install icalendar')
- os_command('venv\\Scripts\\pip', 'install', 'icalendar')
-
-
-def install_markdown():
- log_subheader('Install Markdown')
- os_command('venv\\Scripts\\pip', 'install', 'Markdown')
-
-
-def run_tests():
- log_subheader('Run Tests')
- os_command('venv\\Scripts\\python', 'tools/execute-specs.py', '--write-testlist', 'testlist.txt')
-
-
-def build(build_version, node):
- log_subheader(f'Start Build {build_version or node}')
-
- os.chdir('tools')
- os.system('..\\venv\\Scripts\python generate-mo-files.py')
- log('mo file generated')
-
- os.chdir('winbuildtools')
- os.system('..\\..\\venv\\Scripts\python mod_paths.py .')
- log('paths modified')
-
- os.system(f'..\\..\\venv\\Scripts\python mod_iss_timeline_version.py . {build_version or node}')
- log('version file modified')
-
- log_subheader('Remove old build directory')
- os.system('rmdir /S /Q build >> nul')
- log_subheader('Remove old dist directory')
- os.system('rmdir /S /Q dist >> nul')
- log_subheader('Remove old out directory')
- os.system('del /S /Q inno\out >> nul')
-
- log_subheader('pyinstaller')
- os.system('pyinstaller timeline.spec')
-
- log_subheader('Copying icons and translations to dist')
- os.system('mkdir .\\dist\\icons\\event_icons')
- os.system('mkdir .\\dist\\translations')
- os.system('xcopy /S ..\\..\\icons\*.* .\dist\icons\*.*')
- os.system('xcopy /S ..\\..\\translations\ca\*.* .\dist\\translations\ca\*.*')
- os.system('xcopy /S ..\\..\\translations\de\*.* .\dist\\translations\de\*.*')
- os.system('xcopy /S ..\\..\\translations\el\*.* .\dist\\translations\el\*.*')
- os.system('xcopy /S ..\\..\\translations\es\*.* .\dist\\translations\es\*.*')
- os.system('xcopy /S ..\\..\\translations\eu\*.* .\dist\\translations\eu\*.*')
- os.system('xcopy /S ..\\..\\translations\fr\*.* .\dist\\translations\fr\*.*')
- os.system('xcopy /S ..\\..\\translations\gl\*.* .\dist\\translations\gl\*.*')
- os.system('xcopy /S ..\\..\\translations\he\*.* .\dist\\translations\he\*.*')
- os.system('xcopy /S ..\\..\\translations\it\*.* .\dist\\translations\it\*.*')
- os.system('xcopy /S ..\\..\\translations\ko\*.* .\dist\\translations\ko\*.*')
- os.system('xcopy /S ..\\..\\translations\lt\*.* .\dist\\translations\lt\*.*')
- os.system('xcopy /S ..\\..\\translations\nl\*.* .\dist\\translations\nl\*.*')
- os.system('xcopy /S ..\\..\\translations\pl\*.* .\dist\\translations\pl\*.*')
- os.system('xcopy /S ..\\..\\translations\pt\*.* .\dist\\translations\pt\*.*')
- os.system('xcopy /S ..\\..\\translations\pt_BR\*.* .\dist\\translations\pt_BR\*.*')
- os.system('xcopy /S ..\\..\\translations\ru\*.* .\dist\\translations\ru\*.*')
- os.system('xcopy /S ..\\..\\translations\sv\*.* .\dist\\translations\sv\*.*')
- os.system('xcopy /S ..\\..\\translations\tr\*.* .\dist\\translations\tr\*.*')
- os.system('xcopy /S ..\\..\\translations\vi\*.* .\dist\\translations\vi\*.*')
- os.system('xcopy /S ..\\..\\translations\zh_CH\*.* .\dist\\translations\zh_CH\*.*')
-
- log('Create distributable')
- os.chdir('inno')
- os.system('iscc.exe timeline2Win32.iss')
- log_subheader('DONE')
-
-
-def log_header():
- print('-' * 70)
- print(' jenkins-build-local.py')
- print('-' * 70)
-
-
-def log_subheader(text):
- print(f'----({text})------')
-
-
-def log(text):
- print(f' {text}')
-
-
-def main():
- log_header()
- hg_pull()
- hg_update()
- hg_purge()
- build_version = get_build_version_from_input()
- default_path = get_hg_default_path()
- node = get_hg_node()
- revision = get_hg_rev()
- log(f'Build version: {build_version}')
- log(f'Defult Path : {default_path}')
- log(f'Node : {node}')
- log(f'Revision : {revision}')
-
- log_subheader('Verify version')
- verify_version(build_version, node)
- log('Verified that revision exists')
-
- log_subheader('Log changesets')
- changesets = log_changesets(build_version, node)
- print(changesets)
-
- display_python_version()
- create_virtual_environment()
- upgrade_pip()
-
- install_wxpython()
- install_humblewx()
- install_icalendar()
- install_markdown()
- run_tests()
-
- os.system('venv\\Scripts\pip list')
-
- build(build_version, node)
-
-
-if __name__ == '__main__':
- if 'doctest' in sys.argv:
- import doctest
-
- sys.argv = sys.argv[:-1]
- doctest.testmod()
- else:
- try:
- main()
- except BuildError as ex:
- log('Build aborted')
- log(ex)
diff -r 2d4564f72ba5 -r c6f07716d723 tools/winbuildtools/mod_iss_timeline_version.py
--- a/tools/winbuildtools/mod_iss_timeline_version.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-
-import sys
-import os
-import subprocess
-
-
-USAGE = """
- Usage:
- python mod_iss_timeline_version.py project-tools-dir revision
-"""
-
-
-def get_hash_and_id(revision):
- try:
- return subprocess.check_output([
- "hg", "id",
- "-r", revision,
- ]).decode("utf-8").strip().split(" ", 1)
- except subprocess.CalledProcessError as e:
- print("ERROR:", str(e))
- raise
-
-
-def get_revision_date(revision):
- try:
- return subprocess.check_output([
- "hg", "log",
- "-r", revision,
- "--template", "{date|shortdate}",
- ]).decode("utf-8").strip()
- except subprocess.CalledProcessError as e:
- print("ERROR:", str(e))
- raise
-
-
-def get_version(versionfile):
- with open(versionfile, "r") as f:
- text = f.read()
- lines = text.split("\n")
- for line in lines:
- if line[0:7] == "VERSION":
- break
- line = line.split("(", 1)[1]
- line = line.split(")", 1)[0]
- major, minor, bug = line. split(", ")
- app_ver_name = "Timeline %s.%s.%s" % (major, minor, bug)
- revision = sys.argv[2]
- print("Revision:", revision)
- hash_value, rev_id = get_hash_and_id(sys.argv[2])
- revision_date = get_revision_date(sys.argv[2])
- if rev_id == 'tip':
- output_base_filename = "timeline-%s.%s.%s-beta-%s-%s-Win32Setup" % (major, minor, bug, hash_value, revision_date)
- type = 'TYPE_BETA'
- else:
- output_base_filename = "timeline-%s.%s.%s-Win32Setup" % (major, minor, bug)
- type = 'TYPE_FINAL'
- with open(versionfile, "r") as f:
- text = f.read()
- text = text.replace('TYPE = TYPE_DEV', f'TYPE = {type}')
- text = text.replace('REVISION_HASH = ""', f'REVISION_HASH = "{hash_value}"')
- text = text.replace('REVISION_DATE = ""', f'REVISION_DATE = "{revision_date}"')
- with open(versionfile, "w") as f:
- f.write(text)
- print("[INFO] Version found: %s" % app_ver_name)
- print("[INFO] Filename: %s" % output_base_filename)
- return app_ver_name, output_base_filename
-
-
-def modify_iss_file(target, app_ver_name, output_base_filename):
- with open(target, "r") as f:
- text = f.read()
- with open(target, "w") as f:
- lines = text.split("\n")
- for line in lines:
- if line[0:11] == "AppVerName=":
- line = "AppVerName=%s" % app_ver_name
- f.write(line + "\n")
- print("[INFO] Iss file version line: %s" % line)
- elif line[0:19] == "OutputBaseFilename=":
- line = "OutputBaseFilename=%s" % output_base_filename
- f.write(line + "\n")
- print("[INFO] Iss base filename line: %s" % line)
- else:
- f.write(line + "\n")
-
-
-def main():
- project_dir = sys.argv[1]
- target = os.path.join(project_dir, "inno", "timeline2Win32.iss")
- versionfile_path = os.path.join(project_dir, "..", "..", "source", "timelinelib", "meta", "version.py")
- print("Script: mod2_timeline_iss_win32.py")
- print("Target:", target)
- print("Version:", versionfile_path)
- if not os.path.exists(target):
- print("[ERROR] Can't find target file: %s" % target)
- return
- if not os.path.exists(versionfile_path):
- print("[ERROR] Can't find version file: %s" % versionfile_path)
- return
- app_ver_name, output_base_filename = get_version(versionfile_path)
- modify_iss_file(target, app_ver_name, output_base_filename)
-
-
-if __name__ == "__main__":
- if len(sys.argv) != 3:
- print(USAGE)
- else:
- if not os.path.exists(sys.argv[1]):
- print(USAGE)
- print("[ERROR] Can't find project root dir: %s" % sys.argv[1])
- else:
- main()
diff -r 2d4564f72ba5 -r c6f07716d723 tools/winbuildtools/mod_paths.py
--- a/tools/winbuildtools/mod_paths.py Sat Jul 05 21:51:47 2025 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Rickard Lindberg, Roger Lindberg
-#
-# This file is part of Timeline.
-#
-# Timeline is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# Timeline is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Timeline. If not, see <http://www.gnu.org/licenses/>.
-
-import sys
-import os
-
-
-def main():
- project_dir = sys.argv[1]
- path = os.path.join(
- project_dir, "..", "..", "source", "timelinelib", "config", "paths.py"
- )
- text = None
- with open(path) as f:
- text = f.read()
- collector = []
- for line in text.split("\n"):
- if line.strip().startswith("_ROOT ="):
- line = "_ROOT = '.'"
- collector.append(line.strip())
- text = "\n".join(collector)
- with open(path, "w") as f:
- f.write(text)
- print("paths.py modified")
- print(text)
-
-
-main()
changeset: 7987:2d4564f72ba5
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sat Jul 05 21:51:47 2025 +0200
summary: Extract variables for Python version and wxPython version.
diff -r b21b1bd519b5 -r 2d4564f72ba5 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Sat Jul 05 18:53:16 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Sat Jul 05 21:51:47 2025 +0200
@@ -11,15 +11,18 @@
RUN dnf install -y wget
RUN dnf install -y xorg-x11-server-Xvfb
-RUN wget https://www.python.org/ftp/python/3.13.5/python-3.13.5-amd64.exe
+ENV PYTHON_VERSION=3.13.5
+ENV WXPYTHON_VERSION=4.2.3
+
+RUN wget https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
RUN umask 0 && xvfb-run bash -c '\
- wine python-3.13.5-amd64.exe /quiet TargetDir=$PYTHON_DIR \
+ wine python-$PYTHON_VERSION-amd64.exe /quiet TargetDir=$PYTHON_DIR \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location pyinstaller \
- && wine $PYTHON_PIP_EXE install --no-warn-script-location wxpython==4.2.3 \
+ && wine $PYTHON_PIP_EXE install --no-warn-script-location wxpython==$WXPYTHON_VERSION \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location humblewx \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location markdown \
&& wine $PYTHON_PIP_EXE install --no-warn-script-location icalendar \