mach-nix/mach_nix/run.py
Matías Lang 06a80810d1
Fix AttributeError when nix-build fails (#30)
Since commit fa2bb2d33f, the nix-build
subprocess is called without capture_output=True. Therefore, proc.stderr
is None. The subprocess stderr is already being dumped to stderr, so
there is no need for doing a print.
2020-06-27 12:47:56 +00:00

139 lines
4.8 KiB
Python

import json
import os
import subprocess as sp
import sys
import tempfile
from argparse import ArgumentParser
from os.path import realpath, dirname
from textwrap import dedent
from mach_nix.ensure_nix import ensure_nix
from mach_nix.versions import PyVer
pwd = dirname(realpath(__file__))
def gen(args, return_expr=False):
with open(args.r) as f:
requirements = f.read().strip()
o_file = tempfile.mktemp()
py_ver = PyVer(args.python)
cmd = f'nix-build {pwd}/nix/call_mach.nix -o {o_file}' \
f' --argstr requirements "{requirements}"' \
f' --argstr python_attr python{py_ver.digits()}' \
f' --arg prefer_nixpkgs {json.dumps((not args.prefer_new))}'
proc = sp.run(cmd, shell=True, stdout=sys.stderr)
if proc.returncode:
exit(1)
with open(f"{o_file}/share/mach_nix_file.nix") as src:
expr = src.read()
if return_expr:
return expr
if getattr(args, 'o', None):
with open(args.o, 'w') as dest:
dest.write(expr)
print(f"Expression written to {args.o}")
else:
print(expr)
def env(args):
target_dir = args.directory
expr = gen(args, return_expr=True)
machnix_file = f"{target_dir}/machnix.nix"
shell_nix_file = f"{target_dir}/shell.nix"
default_nix_file = f"{target_dir}/default.nix"
python_nix_file = f"{target_dir}/python.nix"
python_nix_content = dedent(f"""
let
result = import ./machnix.nix;
nixpkgs_commit = "{open(pwd + "/nix/NIXPKGS_COMMIT").read().strip()}";
nixpkgs_sha256 = "{open(pwd + "/nix/NIXPKGS_SHA256").read().strip()}";
pkgs = import (builtins.fetchTarball {{
name = "nixpkgs";
url = "https://github.com/nixos/nixpkgs/tarball/${{nixpkgs_commit}}";
sha256 = nixpkgs_sha256;
}}) {{ config = {{}}; overlays = []; }};
python = pkgs.python37;
manylinux1 = pkgs.pythonManylinuxPackages.manylinux1;
overrides = result.overrides manylinux1 pkgs.autoPatchelfHook;
py = pkgs.python37.override {{ packageOverrides = overrides; }};
in
py.withPackages (ps: result.select_pkgs ps)
""")
if not os.path.isdir(target_dir):
if os.path.exists(target_dir):
print(f'Error: {target_dir} already exists and is not a directory!')
exit(1)
os.mkdir(target_dir)
with open(machnix_file, 'w') as machnix:
machnix.write(expr)
with open(python_nix_file, 'w') as python:
python.write(python_nix_content)
with open(shell_nix_file, 'w') as shell:
shell.write("(import ./python.nix).env\n")
with open(default_nix_file, 'w') as default:
default.write("import ./shell.nix\n")
print(f"\nInitialized python environment in {target_dir}\n"
f"To activate it, execute: 'nix-shell {target_dir}'")
def print_be_patient():
print("Generating python environment... If you run this the first time, the python package index "
"and dependency graph (~200MB) need to be downloaded. Please stay patient!", file=sys.stderr)
def main():
common_arguments = (
(('-p', '--python'), dict(
help='select python version (default: 3.7)',
choices=('2.7', '3.5', '3.6', '3.7', '3.8'),
default='3.7')),
(('-r',), dict(
help='path to requirements.txt file',
metavar='requirements.txt',
required=True)),
(('--prefer-new',), dict(
action='store_true',
help='Prefer newer python package versions instead of the ones from nixpkgs. '
'This might increase build times significantly since no cache can be used'))
)
parser = ArgumentParser()
parser.add_argument('--version', '-V', help='show program version', action='store_true')
subparsers = parser.add_subparsers(dest='command')
gen_parser = subparsers.add_parser('gen', help='generate a nix expression')
gen_parser.add_argument('-o', help='output file. defaults to stdout', metavar='python.nix')
env_parser = subparsers.add_parser('env', help='set up a venv-style environment')
env_parser.add_argument('directory', help='target directory to create the environment')
for p in (gen_parser, env_parser):
for args, kwargs in common_arguments:
p.add_argument(*args, **kwargs)
args = parser.parse_args()
if args.version:
with open(f"{pwd}/VERSION") as f:
print(f"mach-nix: {f.read()}")
exit(0)
if args.command not in ('gen', 'env'):
parser.print_usage()
exit(1)
ensure_nix()
print_be_patient()
if args.command == 'gen':
gen(args)
elif args.command == 'env':
env(args)
if __name__ == "__main__":
main()