import re
import sys
import copy
from pathlib import Path
from collections import defaultdict
crate_deps = dict()
crate_deps_reverse = defaultdict(set)
CHECK_DEV_DEPS = sys.argv[1] == '--dev' if len(sys.argv) > 1 else False
LOCAL_DEP_RE = re.compile(r'{.*path\s*=\s*"([^"]*)"')
TOP_DIR = Path('.').resolve()
members = []
with Path('Cargo.toml').open() as top_cargo:
parsing_members = False
for line in top_cargo.readlines():
if parsing_members:
if line.strip() == ']':
break
if line.strip().startswith('"'):
members.append(Path(line.split('"')[1]))
elif line.strip().startswith("'"):
members.append(Path(line.split("'")[1]))
elif [w.strip() for w in line.split('=')] == ['members', '[']:
parsing_members = True
if len(members) == 0:
print("Failed to parse members from ./Cargo.toml", file=sys.stderr)
sys.exit(127)
for crate_dir in members:
crate_deps[crate_dir] = set()
with (crate_dir / 'Cargo.toml').open() as f:
for line in f.readlines():
if line.strip() == '[dev-dependencies]'.strip() and not CHECK_DEV_DEPS:
break
match = LOCAL_DEP_RE.search(line)
if match is not None:
dep_dir = (crate_dir / match.group(1)).resolve().relative_to(TOP_DIR)
crate_deps[crate_dir].add(dep_dir)
crate_deps_reverse[dep_dir].add(crate_dir)
has_missing_members = False
for dep in set(dep for deps in crate_deps.values() for dep in deps):
if dep not in crate_deps:
has_missing_members = True
print("Member {} is missing in ./Cargo.toml".format(dep), file=sys.stderr)
if has_missing_members:
sys.exit(127)
remember_crate_deps = copy.deepcopy(crate_deps)
while len(crate_deps) > 0:
available = [crate for (crate, deps) in crate_deps.items() if len(deps) == 0]
if len(available) == 0:
print("Loop dependencies detected:\n", file=sys.stderr)
for (k, v) in sorted(crate_deps.items(), key=lambda t: len(t[1])):
print("|- ", k, file=sys.stderr)
for e in v:
print("| |- ", e, file=sys.stderr)
sys.exit(127)
for crate in available:
print(crate)
for crate_user in crate_deps_reverse[crate]:
crate_deps[crate_user].remove(crate)
del crate_deps[crate]
crate_deps = remember_crate_deps
for crate in members:
if len(crate_deps[crate]) > 0:
print("Workspace members are not sorted by dependencies, see the topological sorted printed above", file=sys.stderr)
print("{} depends on following members which come after it:".format(crate), file=sys.stderr)
for e in crate_deps[crate]:
print(" |- ", e, file=sys.stderr)
sys.exit(127)
for crate_user in crate_deps_reverse[crate]:
crate_deps[crate_user].remove(crate)