Source code for pinefarm.install

"""Install tools."""

import os
import pathlib
import shutil
import subprocess
import sys
import tarfile
import tempfile

import lhapdf_management
import pkgconfig
import pygit2
import requests

from . import configs, tools

PINEAPPL_REPO = "https://github.com/N3PDF/pineappl.git"
"Git repo location for pineappl."

LHAPDF_VERSION = "LHAPDF-6.5.4"
"Version of LHAPDF to be used by default (if not already available)."


[docs] def init_prefix(): """Set up paths.""" configs.configs["paths"]["prefix"].mkdir(exist_ok=True) configs.configs["paths"]["bin"].mkdir(exist_ok=True) configs.configs["paths"]["lib"].mkdir(exist_ok=True)
[docs] def is_exe(command: os.PathLike) -> bool: """Check if given path exists and is executable.""" command = pathlib.Path(command) return command.exists() and os.access(command, os.X_OK)
[docs] def mg5amc(): r"""Initialize `MadGraph5_aMC\@NLO <https://code.launchpad.net/mg5amcnlo>`_. Returns ------- bool whether the main executable is now existing. """ from .external import mg5 mg5_exe = configs.configs["commands"]["mg5"] if is_exe(mg5_exe): print("✓ Found mg5amc") return True print("Installing...") dest = configs.configs["paths"]["mg5amc"] # download madgraph in prefix (if not present) with tempfile.TemporaryDirectory() as tmpdir: tmpdir = pathlib.Path(tmpdir) mg5_tar = tmpdir / pathlib.Path(mg5.url()).name # download with requests.get(mg5.url()) as r: mg5_tar.write_bytes(r.content) # extract with tarfile.open(mg5_tar) as tar: tar.extractall(dest) shutil.rmtree(tmpdir) # check if the archive was wrapping a single folder content = list(dest.iterdir()) if len(content) == 1: # in case, remove the intermediate layer for el in content[0].iterdir(): shutil.move(str(el), str(dest)) content[0].rmdir() # in case we're using python3, we need to convert the model file subprocess.run(f"{mg5_exe}", input=mg5.CONVERT_MODEL, encoding="ascii") # retest availability return is_exe(mg5_exe)
[docs] def hawaiian_vrap(): """Install a version of vrap flavoured with pineappl from https://github.com/NNPDF/hawaiian_vrap. Returns ------- bool whether vrap is now installed """ from .external import vrap # Ensure that pineappl and lhapdf are installed _ = lhapdf() _ = pineappl(capi=True) vrapx = configs.configs["commands"]["vrap"] if is_exe(vrapx): print("✓ Found vrap") return True url = f"https://github.com/NNPDF/hawaiian_vrap/archive/refs/tags/{vrap.VERSION}.tar.gz" print(f"Installing the version {vrap.VERSION} of vrap from {url}") with tempfile.TemporaryDirectory() as tmp: tmp_path = pathlib.Path(tmp) vrap_tar = tmp_path / f"hawaiian_vrap-{vrap.VERSION}.tar.gz" with requests.get(url) as r: vrap_tar.write_bytes(r.content) with tarfile.open(vrap_tar, "r:gz") as tar: tar.extractall(tmp_path) # Compile vrap tmp_vrap = tmp_path / f"hawaiian_vrap-{vrap.VERSION}" subprocess.run("autoreconf -fiv", cwd=tmp_vrap / "src", shell=True, check=True) build_dir = tmp_vrap / "build" build_dir.mkdir(exist_ok=True) subprocess.run( ["../src/configure", "--prefix", configs.configs["paths"]["prefix"]], cwd=build_dir, check=True, ) subprocess.run(["make", "install"], cwd=build_dir, check=True) return is_exe(vrapx)
[docs] def cargo(): """Initialize `Rust <https://www.rust-lang.org/>`_ and `Cargo <https://doc.rust-lang.org/stable/cargo/>`_. Returns ------- str path to `cargo` """ # look for existing cargo cargo_exe = shutil.which("cargo") # found, exit if cargo_exe is not None: return cargo_exe cargo_home = configs.configs["paths"]["cargo"] # if there is not a user cargo update environment os.environ["CARGO_HOME"] = str(cargo_home) if cargo_home.is_dir(): return str(cargo_home / "bin" / "cargo") rust_init = configs.configs["paths"]["rust_init"] # if cargo not available let's install with requests.get("https://sh.rustup.rs") as r: with open(rust_init, "wb") as f: f.write(r.content) # install location is controlled by CARGO_HOME variable subprocess.run(f"bash {rust_init} --profile minimal --no-modify-path -y".split()) return str(cargo_home / "bin" / "cargo")
[docs] def pineappl(capi=True, cli=False): """Initialize `PineAPPL <https://github.com/N3PDF/pineappl>`_. Parameters ---------- capi : bool whether to install PineAPPl CAPI (by default `True`, since it's the only thing required) cli : bool whether to install even PineAPPL CLI (by default `False`, since it's not required to run) Returns ------- bool whether `pineappl` and `pineappl_capi` are now available. """ def installed(): """Define availability condition.""" return pkgconfig.exists("pineappl_capi") def cli_installed(): return shutil.which("pineappl") is not None # check if there is something to do at all if (not capi or installed()) and (not cli or cli_installed()): print("✓ Found pineappl") return True print("Installing...") # Ensure lhapdf is installed _ = lhapdf() if capi and not installed(): try: repo = pygit2.Repository(configs.configs["paths"]["pineappl"]) tools.git_pull(repo) except pygit2.GitError: repo = pygit2.clone_repository( PINEAPPL_REPO, configs.configs["paths"]["pineappl"] ) cargo_exe = cargo() subprocess.run([cargo_exe] + "install --force cargo-c".split()) subprocess.run( [cargo_exe] + "cinstall --release --prefix".split() + [ str(configs.configs["paths"]["prefix"]), "--manifest-path=pineappl_capi/Cargo.toml", ], cwd=configs.configs["paths"]["pineappl"], ) if cli and not cli_installed(): cargo_exe = cargo() subprocess.run( [cargo_exe] + "install --path pineappl_cli --root".split() + [str(configs.configs["paths"]["prefix"])], cwd=configs.configs["paths"]["pineappl"], ) configs.configs["commands"]["pineappl"] = shutil.which("pineappl") # retest availability return installed() and (not cli or cli_installed())
[docs] def update_lhapdf_path(path): """Update LHAPDF path, both in environment and `lhapdf_management <https://pypi.org/project/lhapdf-management/>`_. Parameters ---------- path : str or pathlib.Path path to LHAPDF data """ os.environ["LHAPDF_DATA_PATH"] = str(path) lhapdf_management.environment.datapath = pathlib.Path(path)
[docs] def lhapdf_conf(pdf): """Initialize `LHAPDF <https://lhapdf.hepforge.org/>`_. Parameters ---------- pdf : str LHAPDF name of the required PDF """ # user settings *always* take precedence if os.environ.get("LHAPDF_DATA_PATH") is not None: return if shutil.which("lhapdf-config") is not None or pkgconfig.exists("lhapdf"): lhapdf_data = pathlib.Path( subprocess.run("lhapdf-config --datadir".split(), capture_output=True) .stdout.decode() .strip() ) if not lhapdf_data.exists(): lhapdf_data = ( pathlib.Path(pkgconfig.variables("lhapdf")["datarootdir"]).absolute() / "LHAPDF" ) update_lhapdf_path(lhapdf_data) # attempt to determine if it is possible to get the required PDF in the # existing folder (if possible return) try: if os.access(lhapdf_data, os.W_OK) or pdf in ( x.name for x in lhapdf_management.pdf_list("--installed") ): return except PermissionError: pass lhapdf_data = configs.configs["paths"]["lhapdf_data_alternative"] lhapdf_data.mkdir(parents=True, exist_ok=True) shutil.copy2( pathlib.Path(__file__).absolute().parent / "confs" / "lhapdf.conf", lhapdf_data ) update_lhapdf_path(lhapdf_data)
[docs] def lhapdf(): """Install `LHAPDF <https://lhapdf.hepforge.org/>`_ C++ library. This is currently needed by every tool due to postprocessing requirements. """ def installed(): """Define availability condition.""" try: # test python package availability import lhapdf # pylint: disable=unused-import except ModuleNotFoundError: return False return pkgconfig.exists("lhapdf") # check if there is something to do at all if installed(): print("✓ Found lhapdf") return True lhapdf_dest = configs.configs["paths"]["lhapdf"] lhapdf_tar = lhapdf_dest / (LHAPDF_VERSION + ".tar.gz") lhapdf_code = lhapdf_dest / LHAPDF_VERSION lhapdf_dest.mkdir(exist_ok=True) with requests.get( f"https://lhapdf.hepforge.org/downloads/?f={lhapdf_tar.name}" ) as r: with open(lhapdf_tar, "wb") as f: f.write(r.content) with tarfile.open(lhapdf_tar, "r:gz") as tar: tar.extractall(lhapdf_dest) env = os.environ.copy() env["PYTHON"] = sys.executable subprocess.run( f"./configure --prefix={configs.configs['paths']['prefix']}".split(), env=env, cwd=lhapdf_code, ) subprocess.run("make", cwd=lhapdf_code) subprocess.run("make install".split(), cwd=lhapdf_code) return installed()
[docs] def update_environ(): """Adjust necessary environment files.""" def prepend(name, value): if name not in os.environ: os.environ[name] = "" os.environ[name] = str(value) + os.pathsep + os.environ[name] lib = configs.configs["paths"]["lib"] pyver = ".".join(sys.version.split(".")[:2]) # Do both lib and lib64 for python just in case pythonpath = lib / f"python{pyver}" / "site-packages" if not pythonpath.exists(): pythonpath = lib.with_name("lib64") / f"python{pyver}" / "site-packages" prepend("PYTHONPATH", pythonpath) sys.path.insert(0, pythonpath.as_posix()) prepend("PATH", configs.configs["paths"]["bin"]) prepend("LD_LIBRARY_PATH", lib) prepend("PKG_CONFIG_PATH", lib / "pkgconfig")
[docs] def nnlojet(): """Installation of NNLOJET.""" print("Installation not supported yet") return True