-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathhgt
More file actions
executable file
·219 lines (173 loc) · 6.19 KB
/
hgt
File metadata and controls
executable file
·219 lines (173 loc) · 6.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/usr/bin/env python3
"""Hong's Gentoo Tools - manage package.use and package.accept_keywords."""
import argparse
import os
import platform
import re
import socket
import subprocess
import sys
from pathlib import Path
USE_DIR = Path("/etc/portage/package.use")
KEYWORDS_DIR = Path("/etc/portage/package.accept_keywords")
PACKAGE_PATTERN = re.compile(r"^[a-zA-Z0-9_-]+/[a-zA-Z0-9_+-]+$")
USE_FLAG_PATTERN = re.compile(r"^-?[a-zA-Z0-9_+-]+$")
ARCH_MAP = {
"x86_64": "~amd64",
"aarch64": "~arm64",
"armv7l": "~arm",
"i686": "~x86",
"ppc64": "~ppc64",
}
def validate_package(pkg: str) -> bool:
return bool(PACKAGE_PATTERN.match(pkg))
def validate_use_flag(flag: str) -> bool:
return bool(USE_FLAG_PATTERN.match(flag))
def get_arch_keyword() -> str:
arch = platform.machine()
if arch not in ARCH_MAP:
sys.exit(f"Error: Unsupported architecture: {arch}")
return ARCH_MAP[arch]
def get_target_file(base_dir: Path, host: bool) -> Path:
if host:
hostname = socket.gethostname().split(".")[0]
return base_dir / f"60-host-{hostname}"
else:
user = os.environ.get("USER", "unknown")
return base_dir / f"50-user-{user}"
def read_entries(path: Path) -> dict[str, str]:
"""Read file and return {package: rest_of_line} dict."""
entries = {}
if path.exists():
try:
content = path.read_text()
except PermissionError:
content = subprocess.run(
["sudo", "cat", str(path)],
capture_output=True,
text=True,
check=True,
).stdout
for line in content.splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split(None, 1)
if len(parts) >= 1:
pkg = parts[0]
rest = parts[1] if len(parts) > 1 else ""
entries[pkg] = rest
return entries
def merge_use_flags(existing: str, new_flags: list[str]) -> str:
"""Merge USE flags intelligently."""
flag_set: dict[str, bool] = {} # base_flag -> enabled
# Parse existing flags
for flag in existing.split():
if flag.startswith("-"):
flag_set[flag[1:]] = False
else:
flag_set[flag] = True
# Apply new flags
for flag in new_flags:
if flag.startswith("-"):
flag_set[flag[1:]] = False
else:
flag_set[flag] = True
# Build result: enabled flags first (sorted), then disabled (sorted)
enabled = sorted(f for f, v in flag_set.items() if v)
disabled = sorted(f"-{f}" for f, v in flag_set.items() if not v)
return " ".join(enabled + disabled)
def write_entries(path: Path, entries: dict[str, str], stdout: bool) -> None:
"""Write entries to file (sorted by package name) or print to stdout."""
lines = []
for pkg in sorted(entries.keys()):
rest = entries[pkg]
if rest:
lines.append(f"{pkg} {rest}")
else:
lines.append(pkg)
content = "\n".join(lines) + "\n" if lines else ""
if stdout:
print(content, end="")
else:
# Write using sudo tee
proc = subprocess.run(
["sudo", "tee", str(path)],
input=content,
text=True,
capture_output=True,
)
if proc.returncode != 0:
sys.exit(f"Error writing to {path}: {proc.stderr}")
print(f"Written to: {path}")
def cmd_unmask(args) -> None:
pkg = args.package
if not validate_package(pkg):
sys.exit(f"Error: Invalid package format '{pkg}'. Expected: category/package")
keyword = get_arch_keyword()
target = get_target_file(KEYWORDS_DIR, args.host)
entries = read_entries(target)
old_value = entries.get(pkg)
if old_value == keyword:
print(f"Already unmasked: {pkg} {keyword}")
return
entries[pkg] = keyword
write_entries(target, entries, args.stdout)
if not args.stdout:
print(f"Unmasked: {pkg} {keyword}")
def cmd_use(args) -> None:
pkg = args.package
flags = args.flags
if not validate_package(pkg):
sys.exit(f"Error: Invalid package format '{pkg}'. Expected: category/package")
if not flags:
sys.exit("Error: At least one USE flag is required")
for flag in flags:
if not validate_use_flag(flag):
sys.exit(f"Error: Invalid USE flag format '{flag}'")
target = get_target_file(USE_DIR, args.host)
entries = read_entries(target)
existing = entries.get(pkg, "")
merged = merge_use_flags(existing, flags)
if existing == merged:
print(f"USE flags unchanged: {pkg} {merged}")
return
entries[pkg] = merged
write_entries(target, entries, args.stdout)
if not args.stdout:
print(f"USE flags set: {pkg} {merged}")
def main() -> None:
parser = argparse.ArgumentParser(
prog="hgt",
description="Hong's Gentoo Tools - manage portage configuration",
)
subparsers = parser.add_subparsers(dest="command", required=True)
# unmask subcommand
unmask_parser = subparsers.add_parser(
"unmask", help="Add package to package.accept_keywords"
)
unmask_parser.add_argument("package", help="Package name (category/package)")
unmask_parser.add_argument(
"--host", action="store_true", help="Write to host-specific file"
)
unmask_parser.add_argument(
"--stdout", action="store_true", help="Print to stdout instead of writing"
)
unmask_parser.set_defaults(func=cmd_unmask)
# use subcommand
use_parser = subparsers.add_parser("use", help="Set USE flags for a package")
use_parser.add_argument("package", help="Package name (category/package)")
use_parser.add_argument(
"flags", nargs=argparse.REMAINDER, help="USE flags (prefix with - to disable)"
)
use_parser.add_argument(
"--host", action="store_true", help="Write to host-specific file"
)
use_parser.add_argument(
"--stdout", action="store_true", help="Print to stdout instead of writing"
)
use_parser.set_defaults(func=cmd_use)
args = parser.parse_args()
args.func(args)
if __name__ == "__main__":
main()