comparison setup.py @ 42453:94167e701e12

rust: new rust options in setup.py The --rust global option turns on usage (and by default compilation) of the rust-cpython based mercurial.rustext. Similarly to what's previously done for zstd, there is a --no-rust option for the build_ext subcommand in order not to build mercurial.rustext, allowing for an OS distribution to prebuild it. The HGWITHRUSTEXT environment variable is still honored, and has the same effect as before, but now it works mostly by making the --rust global option defaulting to True, with some special cases for the direct-ffi case (see more about that below) Coincidentally, the --rust flag can also be passed from the make commands, like actually all global options, in the PURE variable make local PURE=--rust This feels inappropriate, though, and we should follow up with a proper make variable for that case. Although the direct-ffi bindings aren't directly useful any more, we keep them at this stage because - they provide a short prototyping path for experiments in which a C extension module has to call into a Rust extension. The proper way of doing that would be to use capsules, and it's best to wait for our pull request onto rust-cpython for that: https://github.com/dgrunwald/rust-cpython/pull/169 - Build support for capsules defined in Rust will probably need to reuse some of what's currently in use for direct-ffi.
author Georges Racinet <georges.racinet@octobus.net>
date Thu, 23 May 2019 02:05:32 +0200
parents 810f66b468cd
children f4a65077e949
comparison
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(