|
|
|
@ -24,6 +24,7 @@ import sys |
|
|
|
|
import tarfile |
|
|
|
|
import tempfile |
|
|
|
|
from io import BytesIO |
|
|
|
|
from typing import Dict, Optional |
|
|
|
|
from urllib.request import urlopen |
|
|
|
|
|
|
|
|
|
COMPONENT_PREFIX = "homeassistant.components" |
|
|
|
@ -78,22 +79,23 @@ def get_reqs(components, component): |
|
|
|
|
return requirements |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Store a JSON dump of Nixpkgs' python3Packages |
|
|
|
|
output = subprocess.check_output( |
|
|
|
|
[ |
|
|
|
|
"nix-env", |
|
|
|
|
"-f", |
|
|
|
|
os.path.dirname(sys.argv[0]) + "/../../..", |
|
|
|
|
"-qa", |
|
|
|
|
"-A", |
|
|
|
|
PKG_SET, |
|
|
|
|
"--json", |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
packages = json.loads(output) |
|
|
|
|
def dump_packages() -> Dict[str, Dict[str, str]]: |
|
|
|
|
# Store a JSON dump of Nixpkgs' python3Packages |
|
|
|
|
output = subprocess.check_output( |
|
|
|
|
[ |
|
|
|
|
"nix-env", |
|
|
|
|
"-f", |
|
|
|
|
os.path.dirname(sys.argv[0]) + "/../../..", |
|
|
|
|
"-qa", |
|
|
|
|
"-A", |
|
|
|
|
PKG_SET, |
|
|
|
|
"--json", |
|
|
|
|
] |
|
|
|
|
) |
|
|
|
|
return json.loads(output) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def name_to_attr_path(req): |
|
|
|
|
def name_to_attr_path(req: str, packages: Dict[str, Dict[str, str]]) -> Optional[str]: |
|
|
|
|
attr_paths = set() |
|
|
|
|
names = [req] |
|
|
|
|
# E.g. python-mpd2 is actually called python3.6-mpd2 |
|
|
|
@ -124,41 +126,50 @@ def name_to_attr_path(req): |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
version = get_version() |
|
|
|
|
print("Generating component-packages.nix for version {}".format(version)) |
|
|
|
|
components = parse_components(version=version) |
|
|
|
|
build_inputs = {} |
|
|
|
|
for component in sorted(components.keys()): |
|
|
|
|
attr_paths = [] |
|
|
|
|
missing_reqs = [] |
|
|
|
|
reqs = sorted(get_reqs(components, component)) |
|
|
|
|
for req in reqs: |
|
|
|
|
# Some requirements are specified by url, e.g. https://example.org/foobar#xyz==1.0.0 |
|
|
|
|
# Therefore, if there's a "#" in the line, only take the part after it |
|
|
|
|
req = req[req.find("#") + 1 :] |
|
|
|
|
name = req.split("==")[0] |
|
|
|
|
attr_path = name_to_attr_path(name) |
|
|
|
|
if attr_path is not None: |
|
|
|
|
# Add attribute path without "python3Packages." prefix |
|
|
|
|
attr_paths.append(attr_path[len(PKG_SET + ".") :]) |
|
|
|
|
def main() -> None: |
|
|
|
|
packages = dump_packages() |
|
|
|
|
version = get_version() |
|
|
|
|
print("Generating component-packages.nix for version {}".format(version)) |
|
|
|
|
components = parse_components(version=version) |
|
|
|
|
build_inputs = {} |
|
|
|
|
for component in sorted(components.keys()): |
|
|
|
|
attr_paths = [] |
|
|
|
|
missing_reqs = [] |
|
|
|
|
reqs = sorted(get_reqs(components, component)) |
|
|
|
|
for req in reqs: |
|
|
|
|
# Some requirements are specified by url, e.g. https://example.org/foobar#xyz==1.0.0 |
|
|
|
|
# Therefore, if there's a "#" in the line, only take the part after it |
|
|
|
|
req = req[req.find("#") + 1 :] |
|
|
|
|
name = req.split("==")[0] |
|
|
|
|
attr_path = name_to_attr_path(name, packages) |
|
|
|
|
if attr_path is not None: |
|
|
|
|
# Add attribute path without "python3Packages." prefix |
|
|
|
|
attr_paths.append(attr_path[len(PKG_SET + ".") :]) |
|
|
|
|
else: |
|
|
|
|
missing_reqs.append(name) |
|
|
|
|
else: |
|
|
|
|
missing_reqs.append(name) |
|
|
|
|
else: |
|
|
|
|
build_inputs[component] = attr_paths |
|
|
|
|
n_diff = len(reqs) > len(build_inputs[component]) |
|
|
|
|
if n_diff > 0: |
|
|
|
|
print("Component {} is missing {} dependencies".format(component, n_diff)) |
|
|
|
|
print("missing requirements: {}".format(missing_reqs)) |
|
|
|
|
|
|
|
|
|
with open(os.path.dirname(sys.argv[0]) + "/component-packages.nix", "w") as f: |
|
|
|
|
f.write("# Generated by parse-requirements.py\n") |
|
|
|
|
f.write("# Do not edit!\n\n") |
|
|
|
|
f.write("{\n") |
|
|
|
|
f.write(f' version = "{version}";\n') |
|
|
|
|
f.write(" components = {\n") |
|
|
|
|
for component, attr_paths in build_inputs.items(): |
|
|
|
|
f.write(' "{component}" = ps: with ps; [ ') |
|
|
|
|
f.write(" ".join(attr_paths)) |
|
|
|
|
f.write(" ];\n") |
|
|
|
|
f.write(" };\n") |
|
|
|
|
f.write("}\n") |
|
|
|
|
build_inputs[component] = (attr_paths, missing_reqs) |
|
|
|
|
n_diff = len(reqs) > len(build_inputs[component]) |
|
|
|
|
if n_diff > 0: |
|
|
|
|
print("Component {} is missing {} dependencies".format(component, n_diff)) |
|
|
|
|
print("missing requirements: {}".format(missing_reqs)) |
|
|
|
|
|
|
|
|
|
with open(os.path.dirname(sys.argv[0]) + "/component-packages.nix", "w") as f: |
|
|
|
|
f.write("# Generated by parse-requirements.py\n") |
|
|
|
|
f.write("# Do not edit!\n\n") |
|
|
|
|
f.write("{\n") |
|
|
|
|
f.write(f' version = "{version}";\n') |
|
|
|
|
f.write(" components = {\n") |
|
|
|
|
for component, deps in build_inputs.items(): |
|
|
|
|
available, missing = deps |
|
|
|
|
f.write(f" \"{component}\" = ps: with ps; [ ") |
|
|
|
|
f.write(" ".join(available)) |
|
|
|
|
f.write("];") |
|
|
|
|
if len(missing) > 0: |
|
|
|
|
f.write(f" # missing inputs: {' '.join(missing)}") |
|
|
|
|
f.write("\n") |
|
|
|
|
f.write(" };\n") |
|
|
|
|
f.write("}\n") |
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
main() |
|
|
|
|