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( |