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
1=== modified file 'Makefile'
2--- Makefile 2021-02-19 02:53:40 +0000
3+++ Makefile 2021-11-16 15:36:22 +0000
4@@ -253,7 +253,7 @@
5 $(PYTHON) setup.py build_ext -i -f $(PYTHON_BUILDFLAGS)
6 $(PYTHON) setup.py py2exe > py2exe.log
7 $(PYTHON) tools/win32/ostools.py copytodir tools/win32/start_brz.bat win32_brz.exe
8- $(PYTHON) tools/win32/ostools.py copytodir tools/win32/bazaar.url win32_brz.exe
9+ $(PYTHON) tools/win32/ostools.py copytodir tools/win32/breezy.url win32_brz.exe
10
11 # win32 installer for brz.exe
12 installer: exe copy-docs
13@@ -268,7 +268,8 @@
14
15 copy-docs: docs
16 $(PYTHON) tools/win32/ostools.py copytodir README win32_brz.exe/doc
17- $(PYTHON) tools/win32/ostools.py copytree $(WEB_DOCS) win32_brz.exe
18+ $(PYTHON) tools/win32/ostools.py copydir doc/en/_build/html win32_brz.exe/doc
19+ $(PYTHON) tools/win32/ostools.py copydir doc/developers/_build/html win32_brz.exe/doc/developers
20
21 # clean on win32 all installer-related files and directories
22 clean-win32: clean-docs
23
24=== modified file 'setup.py'
25--- setup.py 2021-08-18 22:38:08 +0000
26+++ setup.py 2021-11-16 15:36:22 +0000
27@@ -366,7 +366,8 @@
28 ico_root = os.path.join(tbzr_root, 'tbreezy', 'resources')
29 icos = [] # list of (path_root, relative_ico_path)
30 # First always brz's icon and its in the root of the brz tree.
31- icos.append(('', 'brz.ico'))
32+ # FIXME: There's no such thing as brz.ico
33+ #icos.append(('', 'brz.ico'))
34 for root, dirs, files in os.walk(ico_root):
35 icos.extend([(ico_root, os.path.join(root, f)[len(ico_root) + 1:])
36 for f in files if f.endswith('.ico')])
37@@ -524,7 +525,7 @@
38
39 elif 'py2exe' in sys.argv:
40 # py2exe setup
41- import py2exe
42+ from py2exe import distutils_buildexe as py2exe
43
44 # pick real brz version
45 import breezy
46@@ -568,15 +569,17 @@
47 self.outfiles.extend([f + 'o' for f in compile_names])
48 # end of class install_data_with_bytecompile
49
50- target = py2exe.build_exe.Target(
51+ target = py2exe.runtime.Target(
52 script="brz",
53 dest_base="brz",
54- icon_resources=[(0, 'brz.ico')],
55+ # FIXME: There's no such thing as brz.ico
56+ #icon_resources=[(0, 'brz.ico')],
57 name=META_INFO['name'],
58 version=version_str,
59 description=META_INFO['description'],
60- author=META_INFO['author'],
61- copyright="(c) Canonical Ltd, 2005-2010",
62+ maintainer=META_INFO['maintainer'],
63+ copyright="Copyright 2005-2012 Canonical Ltd.\n"
64+ "Copyright 2017-2021 Breezy developers",
65 company_name="Canonical Ltd.",
66 comments=META_INFO['description'],
67 )
68@@ -637,6 +640,14 @@
69 # rest of the svn plugin wasn't. So we tell py2exe to leave the
70 # plugins out of the .zip file
71 excludes.extend(["breezy.plugins." + d for d in dirs])
72+ # svn plugin requires subvertpy,
73+ # and pip cannot install it on Windows.
74+ # When subvertpy is not available, remove svn from plugins
75+ if "svn" in dirs:
76+ try:
77+ import subvertpy
78+ except ImportError:
79+ dirs.remove("svn")
80 x = []
81 for i in files:
82 # Throw away files we don't want packaged. Note that plugins may
83@@ -651,16 +662,20 @@
84 if x:
85 target_dir = root[len('breezy/'):] # install to 'plugins/...'
86 plugins_files.append((target_dir, x))
87- # find modules for built-in plugins
88+ # find modules required by built-in plugins
89 import tools.package_mf
90- mf = tools.package_mf.CustomModuleFinder()
91- mf.run_package('breezy/plugins')
92- packs, mods = mf.get_result()
93- additional_packages.update(packs)
94- includes.extend(mods)
95+ mf = tools.package_mf.CustomModuleFinder('.')
96+ mf.load_package_recursive('breezy.plugins')
97+ (packs, mods) = mf.get_result()
98+ # Don't add the plugins packages and modules,
99+ # as they are listed in excluded
100+ additional_packages.update(pack for pack in packs
101+ if not (pack.startswith('breezy.plugins.') or pack in excludes))
102+ includes.extend(mod for mod in mods
103+ if not (mod.startswith('breezy.plugins.') or mod in excludes))
104
105 console_targets = [target,
106- 'tools/win32/bzr_postinstall.py',
107+ 'tools/win32/brz_postinstall.py',
108 ]
109 gui_targets = [gui_target]
110 data_files = topics_files + plugins_files + I18N_FILES
111@@ -715,7 +730,7 @@
112 "includes": includes,
113 "excludes": excludes,
114 "dll_excludes": dll_excludes,
115- "dist_dir": "win32_bzr.exe",
116+ "dist_dir": "win32_brz.exe",
117 "optimize": 2,
118 "custom_boot_script":
119 "tools/win32/py2exe_boot_common.py",
120@@ -725,10 +740,10 @@
121 # We want the libaray.zip to have optimize = 2, but the exe to have
122 # optimize = 1, so that .py files that get compilied at run time
123 # (e.g. user installed plugins) dont have their doc strings removed.
124- class py2exe_no_oo_exe(py2exe.build_exe.py2exe):
125- def build_executable(self, *args, **kwargs):
126+ class py2exe_no_oo_exe(py2exe.py2exe):
127+ def run(self, *args, **kwargs):
128 self.optimize = 1
129- py2exe.build_exe.py2exe.build_executable(self, *args, **kwargs)
130+ super(py2exe_no_oo_exe, self).run(*args, **kwargs)
131 self.optimize = 2
132
133 if __name__ == '__main__':
134
135=== modified file 'tools/package_mf.py'
136--- tools/package_mf.py 2018-11-16 12:25:18 +0000
137+++ tools/package_mf.py 2021-11-16 15:36:22 +0000
138@@ -16,10 +16,12 @@
139
140 """Custom module finder for entire package"""
141
142-import modulefinder
143 import os
144 import sys
145
146+# At present, this is only used on Windows (see setup.py)
147+from py2exe import mf310 as modulefinder
148+
149
150 class CustomModuleFinder(modulefinder.ModuleFinder):
151 """Custom module finder for processing python packages,
152@@ -35,27 +37,54 @@
153 modulefinder.ModuleFinder.__init__(
154 self, path, debug, excludes, replace_paths)
155
156- def run_package(self, package_path):
157- """Recursively process each module in package with run_script method.
158+ def load_package_recursive(self, fqname):
159+ """Recursively process each module in package
160
161- :param package_path: path to package directory.
162+ :param fqname: name of the package.
163 """
164- stack = [package_path]
165+ # Load all the parents
166+ parent = None
167+ path = []
168+ for partname in fqname.split("."):
169+ parent_path = ".".join(path)
170+ path.append(partname)
171+ # import_module works recursively,
172+ # and some of the dependencies may try
173+ # to import modules not present on the system.
174+ # (The actual error is
175+ # AttributeError: 'NoneType' object has no attribute 'is_package')
176+ # Ignore errors here and bail out in the collection loop.
177+ try:
178+ self.import_module(partname, ".".join(path),
179+ self.modules.get(parent_path, None))
180+ except:
181+ pass
182+ stack = [(fqname, parent_path)]
183 while stack:
184- curdir = stack.pop(0)
185- py = os.listdir(curdir)
186- for i in py:
187- full = os.path.join(curdir, i)
188+ (package, parent_path) = stack.pop(0)
189+ # Here we assume that all parents have already been imported.
190+ # Abort when a parent is missing.
191+ parent = self.modules[parent_path]
192+ pkg_module = self.import_module(package, package, parent)
193+ curdir = pkg_module.__file__
194+ dirlist = os.listdir(curdir)
195+ for filename in dirlist:
196+ full = os.path.join(curdir, filename)
197 if os.path.isdir(full):
198+ if filename == "tests":
199+ continue
200 init = os.path.join(full, '__init__.py')
201 if os.path.isfile(init):
202- stack.append(full)
203- continue
204- if not i.endswith('.py'):
205- continue
206- if i == 'setup.py': # skip
207- continue
208- self.run_script(full)
209+ stack.append((".".join((package, filename)), package))
210+ continue
211+ if not filename.endswith('.py'):
212+ continue
213+ if filename == 'setup.py': # skip
214+ continue
215+ # We only accept .py files, so could use [:-3] too - faster...
216+ partname = os.path.splitext(filename)[0]
217+ self.import_module(partname,
218+ ".".join((package, partname)), pkg_module)
219
220 def get_result(self):
221 """Return 2-tuple: (list of packages, list of modules)"""
222
223=== modified file 'tools/win32/brz.iss.cog'
224--- tools/win32/brz.iss.cog 2017-06-02 21:28:05 +0000
225+++ tools/win32/brz.iss.cog 2021-11-16 15:36:22 +0000
226@@ -78,6 +78,14 @@
227 ; [[[cog cog.outl('AppVersion=%s' % VERSION) ]]]
228 ; [[[end]]]
229
230+; [[[cog
231+; import platform
232+;
233+; if platform.machine().endswith("64"):
234+; cog.outl('ArchitecturesAllowed=x64')
235+; cog.outl('ArchitecturesInstallIn64BitMode=x64')
236+; ]]]
237+; [[[end]]]
238 ChangesEnvironment=yes
239 ; MARKH: PrivilegesRequired=none means it can't be installed by a non-admin
240 ; user - but sadly we still need admin - eg, tortoise overlays, installing
241@@ -115,8 +123,12 @@
242 Source: "plugins\*.*"; DestDir: "{app}\\plugins"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete; Components: plugins
243 Source: "*.bat"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
244 Source: "*.url"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
245-Source: "msvc*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
246-Source: "bz*.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
247+; Python3 includes vcruntime*.dll which is not packed in by py2exe.
248+; I'm not sure if the installer has to contain it.
249+; It looks like the binaries work OK without vcruntime dll.
250+; Delete this dll line when we're sure.
251+;Source: "msvc*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
252+Source: "brz*.exe"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
253 Source: "Python*.dll"; DestDir: "{app}"; Flags: ignoreversion restartreplace uninsrestartdelete;
254 Source: "lib\*.*"; DestDir: "{app}\lib"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;
255 Source: "doc\*.*"; DestDir: "{app}\doc"; Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;
256@@ -141,7 +153,7 @@
257 ; imageformats plugins for PyQt4
258 ; [[[cog
259 ; plug_dir = os.path.join(os.path.dirname(cog.inFile), # $(bzr_src_root)/tools/win32
260-; '..', '..', 'win32_bzr.exe', 'imageformats')
261+; '..', '..', 'win32_brz.exe', 'imageformats')
262 ; if os.path.isdir(plug_dir):
263 ; cog.outl('Source: "imageformats\\*.*"; DestDir: "{app}\\imageformats"; '
264 ; 'Flags: createallsubdirs ignoreversion recursesubdirs restartreplace uninsrestartdelete;')
265@@ -179,9 +191,9 @@
266 [Icons]
267 Name: "{group}\Documentation index"; Filename: "{app}\doc\index.html"; WorkingDir: "{app}\doc";
268 Name: "{group}\Breezy Home Page"; Filename: "{app}\breezy.url"; Comment: "https://www.breezy-vcs.org/";
269-Name: "{group}\Start Bzr in cmd shell"; Filename: "{cmd}"; Parameters: "/K start_bzr.bat"; WorkingDir: "{app}"; IconFilename: "{app}\bzr.exe"; Comment: "Open new Bzr session";
270+Name: "{group}\Start Bzr in cmd shell"; Filename: "{cmd}"; Parameters: "/K start_brz.bat"; WorkingDir: "{app}"; IconFilename: "{app}\brz.exe"; Comment: "Open new Bzr session";
271 ; NOTE: Intent is to change the log file location - the line below will need to change to reflect that.
272-Name: "{group}\Open Bzr log file"; Filename: "notepad.exe"; Parameters: "{userdocs}\.bzr.log"; Comment: "Launch notepad to view the bzr log file";
273+Name: "{group}\Open Bzr log file"; Filename: "notepad.exe"; Parameters: "{userdocs}\.brz.log"; Comment: "Launch notepad to view the brz log file";
274
275 ; [[[cog
276 ; if "TBZR" in os.environ:
277@@ -224,7 +236,7 @@
278
279
280 [UninstallRun]
281-Filename: "{app}\bzr_postinstall.exe"; Parameters: "--delete-path --delete-shell-menu --silent"; Flags: skipifdoesntexist runhidden;
282+Filename: "{app}\brz_postinstall.exe"; Parameters: "--delete-path --delete-shell-menu --silent"; Flags: skipifdoesntexist runhidden;
283 ; [[[cog
284 ; if "TBZR" in os.environ:
285 ; cog.outl('Filename: "regsvr32.exe"; Parameters: "/u /s /i: tbzrshellext_x86.dll"; WorkingDir: "{app}"; Components: tortoise; StatusMsg: "Unregistering Tortoise"; Flags: skipifdoesntexist')
286@@ -320,7 +332,7 @@
287 ewWaitUntilTerminated, ErrorCode) then
288 MsgBox('Failed to install TortoiseOverlays: ' + SysErrorMessage(ErrorCode),
289 mbInformation, MB_OK);
290- // Ideally we could be bzr_postinstall.exe this way too, but
291+ // Ideally we could be brz_postinstall.exe this way too, but
292 // its needed at uninstall time.
293 end;
294 // cause explorer to re-fetch handlers.
295
296=== modified file 'tools/win32/info.txt'
297--- tools/win32/info.txt 2010-03-30 20:13:52 +0000
298+++ tools/win32/info.txt 2021-11-16 15:36:22 +0000
299@@ -1,6 +1,6 @@
300-Bazaar is free software; you can redistribute and/or modify it under
301-the terms of the GNU General Public License version 2.
302+Breezy is free software; you can redistribute and/or modify it
303+under the terms of the GNU General Public License version 2.
304
305 See: http://www.gnu.org/copyleft/gpl.html
306
307-Managing source code in Bazaar does not make it subject to the GPL.
308+Managing source code in Breezy does not make it subject to the GPL.
309
310=== modified file 'tools/win32/ostools.py'
311--- tools/win32/ostools.py 2020-01-31 17:43:44 +0000
312+++ tools/win32/ostools.py 2021-11-16 15:36:22 +0000
313@@ -12,6 +12,9 @@
314 ostools.py copytree FILES... DIR
315 copy files to specified directory keeping relative paths
316
317+ ostools.py copydir SOURCE TARGET
318+ recursively copy SOURCE directory tree to TARGET directory
319+
320 ostools.py remove [FILES...] [DIRS...]
321 remove files or directories (recursive)
322 """
323@@ -89,6 +92,19 @@
324
325 return 0
326
327+ if cmd == 'copydir':
328+ if len(argv) != 2:
329+ print("Usage: ostools.py copydir SOURCE TARGET")
330+ return 1
331+
332+ def _copy(src, dest, follow_symlinks=True):
333+ shutil.copy(src, dest, follow_symlinks=follow_symlinks)
334+ print("Copied:", src, "=>", dest)
335+ shutil.copytree(argv[0], argv[1],
336+ copy_function=_copy, dirs_exist_ok=True)
337+
338+ return 0
339+
340 if cmd == 'remove':
341 if len(argv) == 0:
342 print("Usage: ostools.py remove [FILES...] [DIRS...]")
343
344=== modified file 'tools/win32/start_brz.bat'
345--- tools/win32/start_brz.bat 2017-05-21 14:47:52 +0000
346+++ tools/win32/start_brz.bat 2021-11-16 15:36:22 +0000
347@@ -6,7 +6,7 @@
348 REM ******************************************************
349
350 REM Add the Brz directory to system-wide PATH environment variable
351-SET PATH=C:\Program Files\Bazaar;%PATH%
352+SET PATH=C:\Program Files\Breezy;%PATH%
353
354 REM Change next line to set-up e-mail to identify yourself in brz
355 REM SET BZREMAIL=

Subscribers

People subscribed via source and target branches