setup.py
changeset 42453 94167e701e12
parent 42451 810f66b468cd
child 42457 f4a65077e949
equal deleted inserted replaced
42452:a3a8887e4426 42453:94167e701e12
   444             self.make_file([pofile], mobuildfile, spawn, (cmd,))
   444             self.make_file([pofile], mobuildfile, spawn, (cmd,))
   445 
   445 
   446 
   446 
   447 class hgdist(Distribution):
   447 class hgdist(Distribution):
   448     pure = False
   448     pure = False
       
   449     rust = hgrustext is not None
   449     cffi = ispypy
   450     cffi = ispypy
   450 
   451 
   451     global_options = Distribution.global_options + [
   452     global_options = Distribution.global_options + [
   452         ('pure', None, "use pure (slow) Python code instead of C extensions"),
   453         ('pure', None, "use pure (slow) Python code instead of C extensions"),
       
   454         ('rust', None, "use Rust extensions additionally to C extensions"),
   453     ]
   455     ]
   454 
   456 
   455     def has_ext_modules(self):
   457     def has_ext_modules(self):
   456         # self.ext_modules is emptied in hgbuildpy.finalize_options which is
   458         # self.ext_modules is emptied in hgbuildpy.finalize_options which is
   457         # too late for some cases
   459         # too late for some cases
   458         return not self.pure and Distribution.has_ext_modules(self)
   460         return not self.pure and Distribution.has_ext_modules(self)
   459 
   461 
   460 # This is ugly as a one-liner. So use a variable.
   462 # This is ugly as a one-liner. So use a variable.
   461 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
   463 buildextnegops = dict(getattr(build_ext, 'negative_options', {}))
   462 buildextnegops['no-zstd'] = 'zstd'
   464 buildextnegops['no-zstd'] = 'zstd'
       
   465 buildextnegops['no-rust'] = 'rust'
   463 
   466 
   464 class hgbuildext(build_ext):
   467 class hgbuildext(build_ext):
   465     user_options = build_ext.user_options + [
   468     user_options = build_ext.user_options + [
   466         ('zstd', None, 'compile zstd bindings [default]'),
   469         ('zstd', None, 'compile zstd bindings [default]'),
   467         ('no-zstd', None, 'do not compile zstd bindings'),
   470         ('no-zstd', None, 'do not compile zstd bindings'),
       
   471         ('rust', None,
       
   472          'compile Rust extensions if they are in use '
       
   473          '(requires Cargo) [default]'),
       
   474         ('no-rust', None, 'do not compile Rust extensions'),
   468     ]
   475     ]
   469 
   476 
   470     boolean_options = build_ext.boolean_options + ['zstd']
   477     boolean_options = build_ext.boolean_options + ['zstd', 'rust']
   471     negative_opt = buildextnegops
   478     negative_opt = buildextnegops
   472 
   479 
   473     def initialize_options(self):
   480     def initialize_options(self):
   474         self.zstd = True
   481         self.zstd = True
       
   482         self.rust = True
       
   483 
   475         return build_ext.initialize_options(self)
   484         return build_ext.initialize_options(self)
   476 
   485 
   477     def build_extensions(self):
   486     def build_extensions(self):
   478         ruststandalones = [e for e in self.extensions
   487         ruststandalones = [e for e in self.extensions
   479                            if isinstance(e, RustStandaloneExtension)]
   488                            if isinstance(e, RustStandaloneExtension)]
   482         # Filter out zstd if disabled via argument.
   491         # Filter out zstd if disabled via argument.
   483         if not self.zstd:
   492         if not self.zstd:
   484             self.extensions = [e for e in self.extensions
   493             self.extensions = [e for e in self.extensions
   485                                if e.name != 'mercurial.zstd']
   494                                if e.name != 'mercurial.zstd']
   486 
   495 
   487         for rustext in ruststandalones:
   496         # Build Rust standalon extensions if it'll be used
   488             rustext.build('' if self.inplace else self.build_lib)
   497         # and its build is not explictely disabled (for external build
       
   498         # as Linux distributions would do)
       
   499         if self.distribution.rust and self.rust and hgrustext != 'direct-ffi':
       
   500             for rustext in ruststandalones:
       
   501                 rustext.build('' if self.inplace else self.build_lib)
   489 
   502 
   490         return build_ext.build_extensions(self)
   503         return build_ext.build_extensions(self)
   491 
   504 
   492     def build_extension(self, ext):
   505     def build_extension(self, ext):
   493         if isinstance(ext, RustExtension):
   506         if (self.distribution.rust and self.rust
   494             ext.rustbuild()
   507             and isinstance(ext, RustExtension)):
       
   508                 ext.rustbuild()
   495         try:
   509         try:
   496             build_ext.build_extension(self, ext)
   510             build_ext.build_extension(self, ext)
   497         except CCompilerError:
   511         except CCompilerError:
   498             if not getattr(ext, 'optional', False):
   512             if not getattr(ext, 'optional', False):
   499                 raise
   513                 raise
   551 
   565 
   552     def run(self):
   566     def run(self):
   553         basepath = os.path.join(self.build_lib, 'mercurial')
   567         basepath = os.path.join(self.build_lib, 'mercurial')
   554         self.mkpath(basepath)
   568         self.mkpath(basepath)
   555 
   569 
       
   570         rust = self.distribution.rust
   556         if self.distribution.pure:
   571         if self.distribution.pure:
   557             modulepolicy = 'py'
   572             modulepolicy = 'py'
   558         elif self.build_lib == '.':
   573         elif self.build_lib == '.':
   559             # in-place build should run without rebuilding C
   574             # in-place build should run without rebuilding and Rust extensions
   560             # and Rust extensions
   575             modulepolicy = 'rust+c-allow' if rust else 'allow'
   561             if hgrustext == 'cpython':
       
   562                 modulepolicy = 'rust+c-allow'
       
   563             else:
       
   564                 modulepolicy = 'allow'
       
   565         else:
   576         else:
   566             if hgrustext == 'cpython':
   577             modulepolicy = 'rust+c' if rust else 'c'
   567                 modulepolicy = 'rust+c'
       
   568             else:
       
   569                 modulepolicy = 'c'
       
   570 
   578 
   571         content = b''.join([
   579         content = b''.join([
   572             b'# this file is autogenerated by setup.py\n',
   580             b'# this file is autogenerated by setup.py\n',
   573             b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
   581             b'modulepolicy = b"%s"\n' % modulepolicy.encode('ascii'),
   574         ])
   582         ])
  1136     rusttargetdir = os.path.join('rust', 'target', 'release')
  1144     rusttargetdir = os.path.join('rust', 'target', 'release')
  1137 
  1145 
  1138     def __init__(self, mpath, sources, rustlibname, subcrate,
  1146     def __init__(self, mpath, sources, rustlibname, subcrate,
  1139                  py3_features=None, **kw):
  1147                  py3_features=None, **kw):
  1140         Extension.__init__(self, mpath, sources, **kw)
  1148         Extension.__init__(self, mpath, sources, **kw)
  1141         if hgrustext is None:
       
  1142             return
       
  1143         srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
  1149         srcdir = self.rustsrcdir = os.path.join('rust', subcrate)
  1144         self.py3_features = py3_features
  1150         self.py3_features = py3_features
  1145 
  1151 
  1146         # adding Rust source and control files to depends so that the extension
  1152         # adding Rust source and control files to depends so that the extension
  1147         # gets rebuilt if they've changed
  1153         # gets rebuilt if they've changed
  1153             self.depends.extend(os.path.join(dirpath, fname)
  1159             self.depends.extend(os.path.join(dirpath, fname)
  1154                                 for fname in fnames
  1160                                 for fname in fnames
  1155                                 if os.path.splitext(fname)[1] == '.rs')
  1161                                 if os.path.splitext(fname)[1] == '.rs')
  1156 
  1162 
  1157     def rustbuild(self):
  1163     def rustbuild(self):
  1158         if hgrustext is None:
       
  1159             return
       
  1160         env = os.environ.copy()
  1164         env = os.environ.copy()
  1161         if 'HGTEST_RESTOREENV' in env:
  1165         if 'HGTEST_RESTOREENV' in env:
  1162             # Mercurial tests change HOME to a temporary directory,
  1166             # Mercurial tests change HOME to a temporary directory,
  1163             # but, if installed with rustup, the Rust toolchain needs
  1167             # but, if installed with rustup, the Rust toolchain needs
  1164             # HOME to be correct (otherwise the 'no default toolchain'
  1168             # HOME to be correct (otherwise the 'no default toolchain'
  1205         if hgrustext != 'direct-ffi':
  1209         if hgrustext != 'direct-ffi':
  1206             return
  1210             return
  1207         self.extra_compile_args.append('-DWITH_RUST')
  1211         self.extra_compile_args.append('-DWITH_RUST')
  1208         self.libraries.append(rustlibname)
  1212         self.libraries.append(rustlibname)
  1209         self.library_dirs.append(self.rusttargetdir)
  1213         self.library_dirs.append(self.rusttargetdir)
       
  1214 
       
  1215     def rustbuild(self):
       
  1216         if hgrustext == 'direct-ffi':
       
  1217             RustExtension.rustbuild(self)
  1210 
  1218 
  1211 class RustStandaloneExtension(RustExtension):
  1219 class RustStandaloneExtension(RustExtension):
  1212 
  1220 
  1213     def __init__(self, pydottedname, rustcrate, dylibname, **kw):
  1221     def __init__(self, pydottedname, rustcrate, dylibname, **kw):
  1214         RustExtension.__init__(self, pydottedname, [], dylibname, rustcrate,
  1222         RustExtension.__init__(self, pydottedname, [], dylibname, rustcrate,
  1260         'mercurial.thirdparty.zope.interface._zope_interface_coptimizations', [
  1268         'mercurial.thirdparty.zope.interface._zope_interface_coptimizations', [
  1261         'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
  1269         'mercurial/thirdparty/zope/interface/_zope_interface_coptimizations.c',
  1262         ]),
  1270         ]),
  1263     Extension('hgext.fsmonitor.pywatchman.bser',
  1271     Extension('hgext.fsmonitor.pywatchman.bser',
  1264               ['hgext/fsmonitor/pywatchman/bser.c']),
  1272               ['hgext/fsmonitor/pywatchman/bser.c']),
       
  1273     RustStandaloneExtension('mercurial.rustext', 'hg-cpython', 'librusthg',
       
  1274                             py3_features='python3'),
  1265     ]
  1275     ]
  1266 
       
  1267 if hgrustext == 'cpython':
       
  1268     extmodules.append(
       
  1269         RustStandaloneExtension('mercurial.rustext', 'hg-cpython', 'librusthg',
       
  1270                                 py3_features='python3')
       
  1271     )
       
  1272 
  1276 
  1273 
  1277 
  1274 sys.path.insert(0, 'contrib/python-zstandard')
  1278 sys.path.insert(0, 'contrib/python-zstandard')
  1275 import setup_zstd
  1279 import setup_zstd
  1276 extmodules.append(setup_zstd.get_c_extension(
  1280 extmodules.append(setup_zstd.get_c_extension(