This site hosts my projects.
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
tag: tip
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 \
commit 70758ad39bbb716e61c84790f9af5463daedcf79
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sat Jul 5 21:43:23 2025 +0200
Use cp instead of mv to get correct selinux context.
diff --git a/projects2.py b/projects2.py
index 403a421..c10bf24 100755
--- a/projects2.py
+++ b/projects2.py
@@ -507,7 +507,7 @@ class Projects2:
name: '/opt/rlprojects/web/artifacts/timeline/1.0.0'
user: 'scm'
group: 'scm'
- RUN => ['mv', '/opt/rlprojects/tmp/tmp123/file.exe', '/opt/rlprojects/web/artifacts/timeline/1.0.0/file.exe']
+ RUN => ['cp', '/opt/rlprojects/tmp/tmp123/file.exe', '/opt/rlprojects/web/artifacts/timeline/1.0.0/file.exe']
ADD_FILE_EVENT =>
path: '/opt/rlprojects/events/-timeline-1/meta.json'
file: 'timeline/1.0.0/file.exe'
@@ -1286,7 +1286,7 @@ server {{
name: '/opt/rlprojects/web/artifacts/test-project'
user: 'scm'
group: 'scm'
- RUN => ['mv', '/opt/rlprojects/tmp/tmp123/repo/target/file-xyz.zip', '/opt/rlprojects/web/artifacts/test-project/file-xyz.zip']
+ RUN => ['cp', '/opt/rlprojects/tmp/tmp123/repo/target/file-xyz.zip', '/opt/rlprojects/web/artifacts/test-project/file-xyz.zip']
ADD_FILE_EVENT =>
path: '/opt/rlprojects/events/-test-project-1/meta.json'
file: 'test-project/file-xyz.zip'
@@ -1484,7 +1484,7 @@ server {{
user=self.config.SSH_USER,
group=self.config.SSH_USER,
)
- self.process.ensure(["mv", source, absolute_destination])
+ self.process.ensure(["cp", source, absolute_destination])
self.event_service.add_file_to_event(
path=event_path,
file=relpath(
changeset: 7981:e97ea1bdd985
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 04 17:23:47 2025 +0200
summary: Only clean pyc files once.
diff -r 4ab1b37abf3c -r e97ea1bdd985 tools/timelinetools/packaging/archive.py
--- a/tools/timelinetools/packaging/archive.py Thu Jul 03 15:13:50 2025 +0200
+++ b/tools/timelinetools/packaging/archive.py Fri Jul 04 17:23:47 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,6 @@
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 _get_readme_path(self):
return os.path.join(self.get_path(), "README")
changeset: 7986:b21b1bd519b5
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sat Jul 05 18:53:16 2025 +0200
summary: Attempte to modify exe build scripts to work on build server.
diff -r 4c7bbdb609ee -r b21b1bd519b5 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Fri Jul 04 19:55:58 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Sat Jul 05 18:53:16 2025 +0200
@@ -1,34 +1,29 @@
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 winetricks
RUN dnf install -y wget
-
-RUN wget https://www.python.org/ftp/python/3.13.5/python-3.13.5-amd64.exe
-
RUN dnf install -y xorg-x11-server-Xvfb
-ENV WINEPREFIX=/opt/wine
-ENV PYTHON_ROOT=/opt/wine/drive_c/users/root/AppData/Local/Programs/Python/Python313
-
-RUN xvfb-run bash -c "wine python-3.13.5-amd64.exe /quiet && wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/python.exe --version; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location pyinstaller; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location wxpython==4.2.3; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location humblewx; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location markdown; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location icalendar; wineserver -w"
-
+RUN wget https://www.python.org/ftp/python/3.13.5/python-3.13.5-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
-RUN xvfb-run bash -c "wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES && wineserver -w"
+
+RUN umask 0 && xvfb-run bash -c '\
+ wine python-3.13.5-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 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 4c7bbdb609ee -r b21b1bd519b5 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Fri Jul 04 19:55:58 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Sat Jul 05 18:53:16 2025 +0200
@@ -18,6 +18,7 @@
# 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
@@ -69,7 +70,7 @@
archive.clean_pyc_files()
subprocess.check_call([
"wine",
- "/opt/wineprefix/drive_c/users/root/AppData/Local/Programs/Python/Python313/Scripts/pyinstaller.exe",
+ f"{environ['PYTHON_DIR']}\\Scripts\\pyinstaller.exe",
"timeline.spec",
], cwd=archive.get_path("tools/winbuildtools"))
subprocess.check_call([
@@ -85,7 +86,7 @@
])
subprocess.check_call([
"wine",
- "/root/.wine/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe",
+ f"{environ['INNO_DIR']}\\ISCC.exe",
"inno/timeline2Win32.iss",
], cwd=archive.get_path("tools/winbuildtools"))
subprocess.check_call([
commit 81a44eb20ad6d76ffabd6ad5b2980e6b46dafa8d
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sat Jul 5 19:53:30 2025 +0200
Allow builds to take 10 minutes.
Building timeline exe takes longer than one minute.
diff --git a/projects2.py b/projects2.py
index 06bd382..403a421 100755
--- a/projects2.py
+++ b/projects2.py
@@ -1276,7 +1276,7 @@ server {{
LOG => 'Building Dockerfile.ci...'
RUN => ['podman', 'build', '-f', 'Dockerfile.ci', '-q', '.']
LOG => 'Running Dockerfile.ci...'
- RUN => ['timeout', '-k', '5s', '1m', 'podman', 'run', '--init', '--rm', '-v', '/opt/rlprojects/tmp/tmp123/repo:/repo:z', '-w', '/repo', '']
+ RUN => ['timeout', '-k', '5s', '10m', 'podman', 'run', '--init', '--rm', '-v', '/opt/rlprojects/tmp/tmp123/repo:/repo:z', '-w', '/repo', '']
ENSURE_FOLDER =>
name: '/opt/rlprojects/web/test-project'
user: 'scm'
@@ -1391,7 +1391,7 @@ server {{
]).stdout.strip()
self.logger.log(f"Running {basename(dockerfile)}...")
self.process.ensure([
- "timeout", "-k", "5s", "1m",
+ "timeout", "-k", "5s", "10m",
"podman", "run",
"--init",
"--rm",
commit 17032719cba5040d8fef8b5013e6e5027c2f3cfb
Author: Rickard Lindberg <rickard@rickardlindberg.me>
Date: Sat Jul 5 19:52:23 2025 +0200
Use podman instead of docker.
Mainly because it can run without root, and that using wine containers
easier.
diff --git a/projects2.py b/projects2.py
index 042167a..06bd382 100755
--- a/projects2.py
+++ b/projects2.py
@@ -886,9 +886,7 @@ class Projects2:
LOG => 'Setting up tools...'
RUN => ['dnf', 'install', '-y', 'git', 'mercurial', 'tar', 'rsync']
LOG => 'Setting up CI...'
- RUN => ['dnf', 'install', '-y', 'docker-cli']
- RUN => ['systemctl', 'enable', 'docker']
- RUN => ['systemctl', 'start', 'docker']
+ RUN => ['dnf', 'install', '-y', 'podman']
LOG => 'Setting up timezone...'
RUN => ['timedatectl', 'set-timezone', 'Europe/Stockholm']
LOG => 'Configuring project git-project...'
@@ -1035,9 +1033,7 @@ server {{
self.logger.log(f"Setting up tools...")
self.process.ensure(["dnf", "install", "-y", "git", "mercurial", "tar", "rsync"])
self.logger.log(f"Setting up CI...")
- self.process.ensure(["dnf", "install", "-y", "docker-cli"])
- self.process.ensure(["systemctl", "enable", "docker"])
- self.process.ensure(["systemctl", "start", "docker"])
+ self.process.ensure(["dnf", "install", "-y", "podman"])
self.logger.log(f"Setting up timezone...")
self.process.ensure(["timedatectl", "set-timezone", self.config.TIMEZONE])
for project in self.list_projects():
@@ -1277,12 +1273,10 @@ server {{
new: '858b34e097c8563a60db02b19582136ad23057ae'
user_id: None
date: '2025-01-01T12:00:00+00:00'
- RUN => ['id', '-u']
- RUN => ['id', '-g']
LOG => 'Building Dockerfile.ci...'
- RUN => ['sudo', 'docker', 'build', '-f', 'Dockerfile.ci', '-q', '.']
+ RUN => ['podman', 'build', '-f', 'Dockerfile.ci', '-q', '.']
LOG => 'Running Dockerfile.ci...'
- RUN => ['sudo', 'timeout', '-k', '5s', '1m', 'docker', 'run', '--init', '--rm', '-v', '/opt/rlprojects/tmp/tmp123/repo:/repo:z', '-w', '/repo', '--user', ':', '']
+ RUN => ['timeout', '-k', '5s', '1m', 'podman', 'run', '--init', '--rm', '-v', '/opt/rlprojects/tmp/tmp123/repo:/repo:z', '-w', '/repo', '']
ENSURE_FOLDER =>
name: '/opt/rlprojects/web/test-project'
user: 'scm'
@@ -1322,7 +1316,7 @@ server {{
tar_path = join(tmp, "archive.tar")
self.process.ensure(["git", "archive", "--format", "tar", "-o", tar_path, new])
self.process.ensure(["tar", "-x", "-f", tar_path, "-C", repo_path])
- self.run_docker_ci(repo_path, old, new)
+ self.run_ci(repo_path, old, new)
def pretxnchangegroup(self):
"""
@@ -1375,9 +1369,9 @@ server {{
old = self.env.get("HG_NODE")
new = self.env.get("HG_NODE_LAST")
self.process.ensure(["hg", "clone", "-u", new, ".", tmp], capture=False)
- self.run_docker_ci(tmp, old, new)
+ self.run_ci(tmp, old, new)
- def run_docker_ci(self, repo_path, old, new):
+ def run_ci(self, repo_path, old, new):
project = self.env.get(self.config.PROJECT_NAME_ENV_VAR)
event_path = self.event_service.write(path=self.config.EVENTS_ROOT, project=project, event={
"project": project,
@@ -1387,12 +1381,9 @@ server {{
"date": self.clock.isonow(),
})
for dockerfile in self.filesystem.ls(f"{repo_path}/Dockerfile*.ci"):
- user_id = self.process.ensure(['id', '-u']).stdout.strip()
- group_id = self.process.ensure(['id', '-g']).stdout.strip()
self.logger.log(f"Building {basename(dockerfile)}...")
image = self.process.ensure([
- "sudo",
- "docker",
+ "podman",
"build",
"-f", dockerfile,
"-q",
@@ -1400,14 +1391,12 @@ server {{
]).stdout.strip()
self.logger.log(f"Running {basename(dockerfile)}...")
self.process.ensure([
- "sudo",
"timeout", "-k", "5s", "1m",
- "docker", "run",
+ "podman", "run",
"--init",
"--rm",
"-v", f"{repo_path}:/repo:z",
"-w", "/repo",
- "--user", f"{user_id}:{group_id}",
image
], capture=False)
site_root = self.config.get_site_root(project)
changeset: 7981:e97ea1bdd985
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Fri Jul 04 17:23:47 2025 +0200
summary: Only clean pyc files once.
diff -r 4ab1b37abf3c -r e97ea1bdd985 tools/timelinetools/packaging/archive.py
--- a/tools/timelinetools/packaging/archive.py Thu Jul 03 15:13:50 2025 +0200
+++ b/tools/timelinetools/packaging/archive.py Fri Jul 04 17:23:47 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,6 @@
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 _get_readme_path(self):
return os.path.join(self.get_path(), "README")
changeset: 7986:b21b1bd519b5
user: Rickard Lindberg <rickard@rickardlindberg.me>
date: Sat Jul 05 18:53:16 2025 +0200
summary: Attempte to modify exe build scripts to work on build server.
diff -r 4c7bbdb609ee -r b21b1bd519b5 Dockerfile.zip-to-exe.ci
--- a/Dockerfile.zip-to-exe.ci Fri Jul 04 19:55:58 2025 +0200
+++ b/Dockerfile.zip-to-exe.ci Sat Jul 05 18:53:16 2025 +0200
@@ -1,34 +1,29 @@
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 winetricks
RUN dnf install -y wget
-
-RUN wget https://www.python.org/ftp/python/3.13.5/python-3.13.5-amd64.exe
-
RUN dnf install -y xorg-x11-server-Xvfb
-ENV WINEPREFIX=/opt/wine
-ENV PYTHON_ROOT=/opt/wine/drive_c/users/root/AppData/Local/Programs/Python/Python313
-
-RUN xvfb-run bash -c "wine python-3.13.5-amd64.exe /quiet && wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/python.exe --version; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location pyinstaller; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location wxpython==4.2.3; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location humblewx; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location markdown; wineserver -w"
-
-RUN xvfb-run bash -c "wine $PYTHON_ROOT/Scripts/pip.exe install --no-warn-script-location icalendar; wineserver -w"
-
+RUN wget https://www.python.org/ftp/python/3.13.5/python-3.13.5-amd64.exe
RUN wget https://files.innosetup.nl/innosetup-6.4.3.exe
# https://jrsoftware.org/ishelp/index.php?topic=setupcmdline
-RUN xvfb-run bash -c "wine innosetup-6.4.3.exe /SP- /VERYSILENT /SUPPRESSMSGBOXES && wineserver -w"
+
+RUN umask 0 && xvfb-run bash -c '\
+ wine python-3.13.5-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 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 4c7bbdb609ee -r b21b1bd519b5 tools/build-windows-exe-from-source-zip.py
--- a/tools/build-windows-exe-from-source-zip.py Fri Jul 04 19:55:58 2025 +0200
+++ b/tools/build-windows-exe-from-source-zip.py Sat Jul 05 18:53:16 2025 +0200
@@ -18,6 +18,7 @@
# 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
@@ -69,7 +70,7 @@
archive.clean_pyc_files()
subprocess.check_call([
"wine",
- "/opt/wineprefix/drive_c/users/root/AppData/Local/Programs/Python/Python313/Scripts/pyinstaller.exe",
+ f"{environ['PYTHON_DIR']}\\Scripts\\pyinstaller.exe",
"timeline.spec",
], cwd=archive.get_path("tools/winbuildtools"))
subprocess.check_call([
@@ -85,7 +86,7 @@
])
subprocess.check_call([
"wine",
- "/root/.wine/drive_c/Program Files (x86)/Inno Setup 6/ISCC.exe",
+ f"{environ['INNO_DIR']}\\ISCC.exe",
"inno/timeline2Win32.iss",
], cwd=archive.get_path("tools/winbuildtools"))
subprocess.check_call([