Merge lp:~a1s/brz/3.2-windows-installer into lp:brz/3.2

Proposed by Aleksandr Smyshliaev
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~a1s/brz/3.2-windows-installer
Merge into: lp:brz/3.2
Diff against target: 355 lines (+119/-46)
7 files modified
Makefile (+3/-2)
setup.py (+32/-17)
tools/package_mf.py (+45/-16)
tools/win32/brz.iss.cog (+19/-7)
tools/win32/info.txt (+3/-3)
tools/win32/ostools.py (+16/-0)
tools/win32/start_brz.bat (+1/-1)
To merge this branch: bzr merge lp:~a1s/brz/3.2-windows-installer
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+411927@code.launchpad.net

Commit message

Make "make installer" pass on Windows

Description of the change

With these changes, I was able to build Breezy installer package on Windows.

Many commands still don't work, and I wasn't able to compile subvertpy, so the built-in plugin for Subversion is only added when subvertpy exists on the system.

Also, it would be nice to have an icon to pack with Windows executables.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2021-02-19 02:53:40 +0000
+++ Makefile 2021-11-16 15:36:22 +0000
@@ -253,7 +253,7 @@
253 $(PYTHON) setup.py build_ext -i -f $(PYTHON_BUILDFLAGS)253 $(PYTHON) setup.py build_ext -i -f $(PYTHON_BUILDFLAGS)
254 $(PYTHON) setup.py py2exe > py2exe.log254 $(PYTHON) setup.py py2exe > py2exe.log
255 $(PYTHON) tools/win32/ostools.py copytodir tools/win32/start_brz.bat win32_brz.exe255 $(PYTHON) tools/win32/ostools.py copytodir tools/win32/start_brz.bat win32_brz.exe
256 $(PYTHON) tools/win32/ostools.py copytodir tools/win32/bazaar.url win32_brz.exe256 $(PYTHON) tools/win32/ostools.py copytodir tools/win32/breezy.url win32_brz.exe
257257
258# win32 installer for brz.exe258# win32 installer for brz.exe
259installer: exe copy-docs259installer: exe copy-docs
@@ -268,7 +268,8 @@
268268
269copy-docs: docs269copy-docs: docs
270 $(PYTHON) tools/win32/ostools.py copytodir README win32_brz.exe/doc270 $(PYTHON) tools/win32/ostools.py copytodir README win32_brz.exe/doc
271 $(PYTHON) tools/win32/ostools.py copytree $(WEB_DOCS) win32_brz.exe271 $(PYTHON) tools/win32/ostools.py copydir doc/en/_build/html win32_brz.exe/doc
272 $(PYTHON) tools/win32/ostools.py copydir doc/developers/_build/html win32_brz.exe/doc/developers
272273
273# clean on win32 all installer-related files and directories274# clean on win32 all installer-related files and directories
274clean-win32: clean-docs275clean-win32: clean-docs
275276
=== modified file 'setup.py'
--- setup.py 2021-08-18 22:38:08 +0000
+++ setup.py 2021-11-16 15:36:22 +0000
@@ -366,7 +366,8 @@
366 ico_root = os.path.join(tbzr_root, 'tbreezy', 'resources')366 ico_root = os.path.join(tbzr_root, 'tbreezy', 'resources')
367 icos = [] # list of (path_root, relative_ico_path)367 icos = [] # list of (path_root, relative_ico_path)
368 # First always brz's icon and its in the root of the brz tree.368 # First always brz's icon and its in the root of the brz tree.
369 icos.append(('', 'brz.ico'))369 # FIXME: There's no such thing as brz.ico
370 #icos.append(('', 'brz.ico'))
370 for root, dirs, files in os.walk(ico_root):371 for root, dirs, files in os.walk(ico_root):
371 icos.extend([(ico_root, os.path.join(root, f)[len(ico_root) + 1:])372 icos.extend([(ico_root, os.path.join(root, f)[len(ico_root) + 1:])
372 for f in files if f.endswith('.ico')])373 for f in files if f.endswith('.ico')])
@@ -524,7 +525,7 @@
524525
525elif 'py2exe' in sys.argv:526elif 'py2exe' in sys.argv:
526 # py2exe setup527 # py2exe setup
527 import py2exe528 from py2exe import distutils_buildexe as py2exe
528529
529 # pick real brz version530 # pick real brz version
530 import breezy531 import breezy
@@ -568,15 +569,17 @@
568 self.outfiles.extend([f + 'o' for f in compile_names])569 self.outfiles.extend([f + 'o' for f in compile_names])
569 # end of class install_data_with_bytecompile570 # end of class install_data_with_bytecompile
570571
571 target = py2exe.build_exe.Target(572 target = py2exe.runtime.Target(
572 script="brz",573 script="brz",
573 dest_base="brz",574 dest_base="brz",
574 icon_resources=[(0, 'brz.ico')],575 # FIXME: There's no such thing as brz.ico
576 #icon_resources=[(0, 'brz.ico')],
575 name=META_INFO['name'],577 name=META_INFO['name'],
576 version=version_str,578 version=version_str,
577 description=META_INFO['description'],579 description=META_INFO['description'],
578 author=META_INFO['author'],580 maintainer=META_INFO['maintainer'],
579 copyright="(c) Canonical Ltd, 2005-2010",581 copyright="Copyright 2005-2012 Canonical Ltd.\n"
582 "Copyright 2017-2021 Breezy developers",
580 company_name="Canonical Ltd.",583 company_name="Canonical Ltd.",
581 comments=META_INFO['description'],584 comments=META_INFO['description'],
582 )585 )
@@ -637,6 +640,14 @@
637 # rest of the svn plugin wasn't. So we tell py2exe to leave the640 # rest of the svn plugin wasn't. So we tell py2exe to leave the
638 # plugins out of the .zip file641 # plugins out of the .zip file
639 excludes.extend(["breezy.plugins." + d for d in dirs])642 excludes.extend(["breezy.plugins." + d for d in dirs])
643 # svn plugin requires subvertpy,
644 # and pip cannot install it on Windows.
645 # When subvertpy is not available, remove svn from plugins
646 if "svn" in dirs:
647 try:
648 import subvertpy
649 except ImportError:
650 dirs.remove("svn")
640 x = []651 x = []
641 for i in files:652 for i in files:
642 # Throw away files we don't want packaged. Note that plugins may653 # Throw away files we don't want packaged. Note that plugins may
@@ -651,16 +662,20 @@
651 if x:662 if x:
652 target_dir = root[len('breezy/'):] # install to 'plugins/...'663 target_dir = root[len('breezy/'):] # install to 'plugins/...'
653 plugins_files.append((target_dir, x))664 plugins_files.append((target_dir, x))
654 # find modules for built-in plugins665 # find modules required by built-in plugins
655 import tools.package_mf666 import tools.package_mf
656 mf = tools.package_mf.CustomModuleFinder()667 mf = tools.package_mf.CustomModuleFinder('.')
657 mf.run_package('breezy/plugins')668 mf.load_package_recursive('breezy.plugins')
658 packs, mods = mf.get_result()669 (packs, mods) = mf.get_result()
659 additional_packages.update(packs)670 # Don't add the plugins packages and modules,
660 includes.extend(mods)671 # as they are listed in excluded
672 additional_packages.update(pack for pack in packs
673 if not (pack.startswith('breezy.plugins.') or pack in excludes))
674 includes.extend(mod for mod in mods
675 if not (mod.startswith('breezy.plugins.') or mod in excludes))
661676
662 console_targets = [target,677 console_targets = [target,
663 'tools/win32/bzr_postinstall.py',678 'tools/win32/brz_postinstall.py',
664 ]679 ]
665 gui_targets = [gui_target]680 gui_targets = [gui_target]
666 data_files = topics_files + plugins_files + I18N_FILES681 data_files = topics_files + plugins_files + I18N_FILES
@@ -715,7 +730,7 @@
715 "includes": includes,730 "includes": includes,
716 "excludes": excludes,731 "excludes": excludes,
717 "dll_excludes": dll_excludes,732 "dll_excludes": dll_excludes,
718 "dist_dir": "win32_bzr.exe",733 "dist_dir": "win32_brz.exe",
719 "optimize": 2,734 "optimize": 2,
720 "custom_boot_script":735 "custom_boot_script":
721 "tools/win32/py2exe_boot_common.py",736 "tools/win32/py2exe_boot_common.py",
@@ -725,10 +740,10 @@
725 # We want the libaray.zip to have optimize = 2, but the exe to have740 # We want the libaray.zip to have optimize = 2, but the exe to have
726 # optimize = 1, so that .py files that get compilied at run time741 # optimize = 1, so that .py files that get compilied at run time
727 # (e.g. user installed plugins) dont have their doc strings removed.742 # (e.g. user installed plugins) dont have their doc strings removed.
728 class py2exe_no_oo_exe(py2exe.build_exe.py2exe):743 class py2exe_no_oo_exe(py2exe.py2exe):
729 def build_executable(self, *args, **kwargs):744 def run(self, *args, **kwargs):
730 self.optimize = 1745 self.optimize = 1
731 py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)746 super(py2exe_no_oo_exe, self).run(*args, **kwargs)
732 self.optimize = 2747 self.optimize = 2
733748
734 if __name__ == '__main__':749 if __name__ == '__main__':
735750
=== modified file 'tools/package_mf.py'
--- tools/package_mf.py 2018-11-16 12:25:18 +0000
+++ tools/package_mf.py 2021-11-16 15:36:22 +0000
@@ -16,10 +16,12 @@
1616
17"""Custom module finder for entire package"""17"""Custom module finder for entire package"""
1818
19import modulefinder
20import os19import os
21import sys20import sys
2221
22# At present, this is only used on Windows (see setup.py)
23from py2exe import mf310 as modulefinder
24
2325
24class CustomModuleFinder(modulefinder.ModuleFinder):26class CustomModuleFinder(modulefinder.ModuleFinder):
25 """Custom module finder for processing python packages,27 """Custom module finder for processing python packages,
@@ -35,27 +37,54 @@
35 modulefinder.ModuleFinder.__init__(37 modulefinder.ModuleFinder.__init__(
36 self, path, debug, excludes, replace_paths)38 self, path, debug, excludes, replace_paths)
3739
38 def run_package(self, package_path):40 def load_package_recursive(self, fqname):
39 """Recursively process each module in package with run_script method.41 """Recursively process each module in package
4042
41 :param package_path: path to package directory.43 :param fqname: name of the package.
42 """44 """
43 stack = [package_path]45 # Load all the parents
46 parent = None
47 path = []
48 for partname in fqname.split("."):
49 parent_path = ".".join(path)
50 path.append(partname)
51 # import_module works recursively,
52 # and some of the dependencies may try
53 # to import modules not present on the system.
54 # (The actual error is
55 # AttributeError: 'NoneType' object has no attribute 'is_package')
56 # Ignore errors here and bail out in the collection loop.
57 try:
58 self.import_module(partname, ".".join(path),
59 self.modules.get(parent_path, None))
60 except:
61 pass
62 stack = [(fqname, parent_path)]
44 while stack:63 while stack:
45 curdir = stack.pop(0)64 (package, parent_path) = stack.pop(0)
46 py = os.listdir(curdir)65 # Here we assume that all parents have already been imported.
47 for i in py:66 # Abort when a parent is missing.
48 full = os.path.join(curdir, i)67 parent = self.modules[parent_path]
68 pkg_module = self.import_module(package, package, parent)
69 curdir = pkg_module.__file__
70 dirlist = os.listdir(curdir)
71 for filename in dirlist:
72 full = os.path.join(curdir, filename)
49 if os.path.isdir(full):73 if os.path.isdir(full):
74 if filename == "tests":
75 continue
50 init = os.path.join(full, '__init__.py')76 init = os.path.join(full, '__init__.py')
51 if os.path.isfile(init):77 if os.path.isfile(init):
52 stack.append(full)78 stack.append((".".join((package, filename)), package))
53 continue79 continue
54 if not i.endswith('.py'):80 if not filename.endswith('.py'):
55 continue81 continue
56 if i == 'setup.py': # skip82 if filename == 'setup.py': # skip
57 continue83 continue
58 self.run_script(full)84 # We only accept .py files, so could use [:-3] too - faster...
85 partname = os.path.splitext(filename)[0]
86 self.import_module(partname,
87 ".".join((package, partname)), pkg_module)
5988
60 def get_result(self):89 def get_result(self):
61 """Return 2-tuple: (list of packages, list of modules)"""90 """Return 2-tuple: (list of packages, list of modules)"""
6291
=== modified file 'tools/win32/brz.iss.cog'
--- tools/win32/brz.iss.cog 2017-06-02 21:28:05 +0000
+++ tools/win32/brz.iss.cog 2021-11-16 15:36:22 +0000
@@ -78,6 +78,14 @@
78; [[[cog cog.outl('AppVersion=%s' % VERSION) ]]]78; [[[cog cog.outl('AppVersion=%s' % VERSION) ]]]
79; [[[end]]]79; [[[end]]]
8080
81; [[[cog
82; import platform
83;
84; if platform.machine().endswith("64"):
85; cog.outl('ArchitecturesAllowed=x64')
86; cog.outl('ArchitecturesInstallIn64BitMode=x64')
87; ]]]
88; [[[end]]]
81ChangesEnvironment=yes89ChangesEnvironment=yes
82; MARKH: PrivilegesRequired=none means it can't be installed by a non-admin90; MARKH: PrivilegesRequired=none means it can't be installed by a non-admin
83; user - but sadly we still need admin - eg, tortoise overlays, installing91; user - but sadly we still need admin - eg, tortoise overlays, installing
@@ -115,8 +123,12 @@
115Source: "plugins\*.*"; DestDir: "{app}\\plugins"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete; Components: plugins123Source: "plugins\*.*"; DestDir: "{app}\\plugins"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete; Components: plugins
116Source: "*.bat"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;124Source: "*.bat"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
117Source: "*.url"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;125Source: "*.url"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
118Source: "msvc*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;126; Python3 includes vcruntime*.dll which is not packed in by py2exe.
119Source: "bz*.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;127; I'm not sure if the installer has to contain it.
128; It looks like the binaries work OK without vcruntime dll.
129; Delete this dll line when we're sure.
130;Source: "msvc*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
131Source: "brz*.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
120Source: "Python*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;132Source: "Python*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
121Source: "lib\*.*"; DestDir: "{app}\lib"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;133Source: "lib\*.*"; DestDir: "{app}\lib"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;
122Source: "doc\*.*"; DestDir: "{app}\doc"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;134Source: "doc\*.*"; DestDir: "{app}\doc"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;
@@ -141,7 +153,7 @@
141; imageformats plugins for PyQt4153; imageformats plugins for PyQt4
142; [[[cog154; [[[cog
143; plug_dir = os.path.join(os.path.dirname(cog.inFile), # $(bzr_src_root)/tools/win32155; plug_dir = os.path.join(os.path.dirname(cog.inFile), # $(bzr_src_root)/tools/win32
144; '..', '..', 'win32_bzr.exe', 'imageformats')156; '..', '..', 'win32_brz.exe', 'imageformats')
145; if os.path.isdir(plug_dir):157; if os.path.isdir(plug_dir):
146; cog.outl('Source: "imageformats\\*.*"; DestDir: "{app}\\imageformats"; '158; cog.outl('Source: "imageformats\\*.*"; DestDir: "{app}\\imageformats"; '
147; 'Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;')159; 'Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;')
@@ -179,9 +191,9 @@
179[Icons]191[Icons]
180Name: "{group}\Documentation index"; Filename: "{app}\doc\index.html"; WorkingDir: "{app}\doc";192Name: "{group}\Documentation index"; Filename: "{app}\doc\index.html"; WorkingDir: "{app}\doc";
181Name: "{group}\Breezy Home Page"; Filename: "{app}\breezy.url"; Comment: "https://www.breezy-vcs.org/";193Name: "{group}\Breezy Home Page"; Filename: "{app}\breezy.url"; Comment: "https://www.breezy-vcs.org/";
182Name: "{group}\Start Bzr in cmd shell"; Filename: "{cmd}"; Parameters: "/K start_bzr.bat"; WorkingDir: "{app}"; IconFilename: "{app}\bzr.exe"; Comment: "Open new Bzr session";194Name: "{group}\Start Bzr in cmd shell"; Filename: "{cmd}"; Parameters: "/K start_brz.bat"; WorkingDir: "{app}"; IconFilename: "{app}\brz.exe"; Comment: "Open new Bzr session";
183; NOTE: Intent is to change the log file location - the line below will need to change to reflect that.195; NOTE: Intent is to change the log file location - the line below will need to change to reflect that.
184Name: "{group}\Open Bzr log file"; Filename: "notepad.exe"; Parameters: "{userdocs}\.bzr.log"; Comment: "Launch notepad to view the bzr log file";196Name: "{group}\Open Bzr log file"; Filename: "notepad.exe"; Parameters: "{userdocs}\.brz.log"; Comment: "Launch notepad to view the brz log file";
185197
186; [[[cog198; [[[cog
187; if "TBZR" in os.environ:199; if "TBZR" in os.environ:
@@ -224,7 +236,7 @@
224236
225237
226[UninstallRun]238[UninstallRun]
227Filename: "{app}\bzr_postinstall.exe"; Parameters: "--delete-path --delete-shell-menu --silent"; Flags: skipifdoesntexist runhidden;239Filename: "{app}\brz_postinstall.exe"; Parameters: "--delete-path --delete-shell-menu --silent"; Flags: skipifdoesntexist runhidden;
228; [[[cog240; [[[cog
229; if "TBZR" in os.environ:241; if "TBZR" in os.environ:
230; cog.outl('Filename: "regsvr32.exe"; Parameters: "/u /s /i: tbzrshellext_x86.dll"; WorkingDir: "{app}"; Components: tortoise; StatusMsg: "Unregistering Tortoise"; Flags: skipifdoesntexist')242; cog.outl('Filename: "regsvr32.exe"; Parameters: "/u /s /i: tbzrshellext_x86.dll"; WorkingDir: "{app}"; Components: tortoise; StatusMsg: "Unregistering Tortoise"; Flags: skipifdoesntexist')
@@ -320,7 +332,7 @@
320 ewWaitUntilTerminated, ErrorCode) then332 ewWaitUntilTerminated, ErrorCode) then
321 MsgBox('Failed to install TortoiseOverlays: ' + SysErrorMessage(ErrorCode),333 MsgBox('Failed to install TortoiseOverlays: ' + SysErrorMessage(ErrorCode),
322 mbInformation, MB_OK);334 mbInformation, MB_OK);
323 // Ideally we could be bzr_postinstall.exe this way too, but335 // Ideally we could be brz_postinstall.exe this way too, but
324 // its needed at uninstall time.336 // its needed at uninstall time.
325 end;337 end;
326 // cause explorer to re-fetch handlers.338 // cause explorer to re-fetch handlers.
327339
=== modified file 'tools/win32/info.txt'
--- tools/win32/info.txt 2010-03-30 20:13:52 +0000
+++ tools/win32/info.txt 2021-11-16 15:36:22 +0000
@@ -1,6 +1,6 @@
1Bazaar is free software; you can redistribute and/or modify it under1Breezy is free software; you can redistribute and/or modify it
2the terms of the GNU General Public License version 2. 2under the terms of the GNU General Public License version 2.
33
4See: http://www.gnu.org/copyleft/gpl.html4See: http://www.gnu.org/copyleft/gpl.html
55
6Managing source code in Bazaar does not make it subject to the GPL.6Managing source code in Breezy does not make it subject to the GPL.
77
=== modified file 'tools/win32/ostools.py'
--- tools/win32/ostools.py 2020-01-31 17:43:44 +0000
+++ tools/win32/ostools.py 2021-11-16 15:36:22 +0000
@@ -12,6 +12,9 @@
12 ostools.py copytree FILES... DIR12 ostools.py copytree FILES... DIR
13 copy files to specified directory keeping relative paths13 copy files to specified directory keeping relative paths
1414
15 ostools.py copydir SOURCE TARGET
16 recursively copy SOURCE directory tree to TARGET directory
17
15 ostools.py remove [FILES...] [DIRS...]18 ostools.py remove [FILES...] [DIRS...]
16 remove files or directories (recursive)19 remove files or directories (recursive)
17"""20"""
@@ -89,6 +92,19 @@
8992
90 return 093 return 0
9194
95 if cmd == 'copydir':
96 if len(argv) != 2:
97 print("Usage: ostools.py copydir SOURCE TARGET")
98 return 1
99
100 def _copy(src, dest, follow_symlinks=True):
101 shutil.copy(src, dest, follow_symlinks=follow_symlinks)
102 print("Copied:", src, "=>", dest)
103 shutil.copytree(argv[0], argv[1],
104 copy_function=_copy, dirs_exist_ok=True)
105
106 return 0
107
92 if cmd == 'remove':108 if cmd == 'remove':
93 if len(argv) == 0:109 if len(argv) == 0:
94 print("Usage: ostools.py remove [FILES...] [DIRS...]")110 print("Usage: ostools.py remove [FILES...] [DIRS...]")
95111
=== modified file 'tools/win32/start_brz.bat'
--- tools/win32/start_brz.bat 2017-05-21 14:47:52 +0000
+++ tools/win32/start_brz.bat 2021-11-16 15:36:22 +0000
@@ -6,7 +6,7 @@
6REM ******************************************************6REM ******************************************************
77
8REM Add the Brz directory to system-wide PATH environment variable8REM Add the Brz directory to system-wide PATH environment variable
9SET PATH=C:\Program Files\Bazaar;%PATH%9SET PATH=C:\Program Files\Breezy;%PATH%
1010
11REM Change next line to set-up e-mail to identify yourself in brz11REM Change next line to set-up e-mail to identify yourself in brz
12REM SET BZREMAIL=12REM SET BZREMAIL=

Subscribers

People subscribed via source and target branches