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