windows: add an experimental option for long paths support
This commit adds an experimental --long-paths-support flag to build_hgexe
on Windows. It is off by default, but when supplied, causes setup.py to
embed some XML into the generated hg.exe, which in turn tells Windows to
allow this exe to use long paths (given that the appropriate registry setting
is enabled as well).
This was tested on Windows 10 14393 and 15063.
This commit introduces a badly-named initialize_options function, but its name
is dictated by distutils, rather than chosen.
# no-check-commit
--- a/setup.py Sun Oct 08 22:07:47 2017 +0200
+++ b/setup.py Mon Oct 09 02:30:23 2017 -0700
@@ -516,6 +516,26 @@
class buildhgexe(build_ext):
description = 'compile hg.exe from mercurial/exewrapper.c'
+ user_options = build_ext.user_options + [
+ ('long-paths-support', None, 'enable support for long paths on '
+ 'Windows (off by default and '
+ 'experimental)'),
+ ]
+
+ LONG_PATHS_MANIFEST = """
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <application>
+ <windowsSettings
+ xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
+ <ws2:longPathAware>true</ws2:longPathAware>
+ </windowsSettings>
+ </application>
+ </assembly>"""
+
+ def initialize_options(self):
+ build_ext.initialize_options(self)
+ self.long_paths_support = False
def build_extensions(self):
if os.name != 'nt':
@@ -557,10 +577,45 @@
objects = self.compiler.compile(['mercurial/exewrapper.c'],
output_dir=self.build_temp)
dir = os.path.dirname(self.get_ext_fullpath('dummy'))
- target = os.path.join(dir, 'hg')
- self.compiler.link_executable(objects, target,
+ self.hgtarget = os.path.join(dir, 'hg')
+ self.compiler.link_executable(objects, self.hgtarget,
libraries=[],
output_dir=self.build_temp)
+ if self.long_paths_support:
+ self.addlongpathsmanifest()
+
+ def addlongpathsmanifest(self):
+ """Add manifest pieces so that hg.exe understands long paths
+
+ This is an EXPERIMENTAL feature, use with care.
+ To enable long paths support, one needs to do two things:
+ - build Mercurial with --long-paths-support option
+ - change HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\
+ LongPathsEnabled to have value 1.
+
+ Please ignore 'warning 81010002: Unrecognized Element "longPathAware"';
+ it happens because Mercurial uses mt.exe circa 2008, which is not
+ yet aware of long paths support in the manifest (I think so at least).
+ This does not stop mt.exe from embedding/merging the XML properly.
+
+ Why resource #1 should be used for .exe manifests? I don't know and
+ wasn't able to find an explanation for mortals. But it seems to work.
+ """
+ exefname = self.compiler.executable_filename(self.hgtarget)
+ fdauto, manfname = tempfile.mkstemp(suffix='.hg.exe.manifest')
+ os.close(fdauto)
+ with open(manfname, 'w') as f:
+ f.write(self.LONG_PATHS_MANIFEST)
+ log.info("long paths manifest is written to '%s'" % manfname)
+ inputresource = '-inputresource:%s;#1' % exefname
+ outputresource = '-outputresource:%s;#1' % exefname
+ log.info("running mt.exe to update hg.exe's manifest in-place")
+ # supplying both -manifest and -inputresource to mt.exe makes
+ # it merge the embedded and supplied manifests in the -outputresource
+ self.spawn(['mt.exe', '-nologo', '-manifest', manfname,
+ inputresource, outputresource])
+ log.info("done updating hg.exe's manifest")
+ os.remove(manfname)
@property
def hgexepath(self):