Skip to content

Commit 74250bc

Browse files
committed
--verbose
1 parent 2437a89 commit 74250bc

File tree

14 files changed

+223
-60
lines changed

14 files changed

+223
-60
lines changed

bin/pocket-build.py

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
# Full text: https://github.com/apathetic-tools/pocket-build/blob/main/LICENSE
55

66
# Version: 0.1.0
7-
# Commit: f7e8c12
7+
# Commit: 2437a89
88
# Repo: https://github.com/apathetic-tools/pocket-build
99

1010
"""
1111
Pocket Build — a tiny build system that fits in your pocket.
1212
This single-file version is auto-generated from modular sources.
1313
Version: 0.1.0
14-
Commit: f7e8c12
14+
Commit: 2437a89
1515
"""
1616

1717
from __future__ import annotations
@@ -30,8 +30,10 @@
3030

3131
from typing_extensions import NotRequired
3232

33-
3433
# === types.py ===
34+
# src/pocket_build/types.py
35+
36+
3537
class IncludeEntry(TypedDict, total=False):
3638
src: str
3739
dest: NotRequired[str]
@@ -48,6 +50,8 @@ class RootConfig(TypedDict, total=False):
4850

4951

5052
# === utils.py ===
53+
# src/pocket_build/utils.py
54+
5155
# Terminal colors (ANSI)
5256
GREEN = "\033[92m"
5357
YELLOW = "\033[93m"
@@ -75,6 +79,9 @@ def is_excluded(path: Path, exclude_patterns: List[str], root: Path) -> bool:
7579

7680

7781
# === config.py ===
82+
# src/pocket_build/config.py
83+
84+
7885
def parse_builds(raw_config: Dict[str, Any]) -> List[BuildConfig]:
7986
builds = raw_config.get("builds")
8087
if isinstance(builds, list):
@@ -83,41 +90,61 @@ def parse_builds(raw_config: Dict[str, Any]) -> List[BuildConfig]:
8390

8491

8592
# === build.py ===
86-
def copy_file(src: Path, dest: Path, root: Path) -> None:
93+
# src/pocket_build/build.py
94+
95+
96+
def copy_file(src: Path, dest: Path, root: Path, verbose: bool = False) -> None:
8797
dest.parent.mkdir(parents=True, exist_ok=True)
8898
shutil.copy2(src, dest)
89-
print(f"📄 {src.relative_to(root)}{dest.relative_to(root)}")
99+
if verbose:
100+
print(f"📄 {src.relative_to(root)}{dest.relative_to(root)}")
90101

91102

92103
def copy_directory(
93-
src: Path, dest: Path, exclude_patterns: List[str], root: Path
104+
src: Path,
105+
dest: Path,
106+
exclude_patterns: List[str],
107+
root: Path,
108+
verbose: bool = False,
94109
) -> None:
95110
"""Recursively copy directory contents, skipping excluded files."""
96111
for item in src.rglob("*"):
97112
if is_excluded(item, exclude_patterns, root):
98-
print(f"🚫 Skipped: {item.relative_to(root)}")
113+
if verbose:
114+
print(f"🚫 Skipped: {item.relative_to(root)}")
99115
continue
100116
target = dest / item.relative_to(src)
101117
if item.is_dir():
102118
target.mkdir(parents=True, exist_ok=True)
103119
else:
104120
target.parent.mkdir(parents=True, exist_ok=True)
105121
shutil.copy2(item, target)
106-
print(f"{GREEN}📄{RESET} {item.relative_to(root)}")
122+
if verbose:
123+
print(f"{GREEN}📄{RESET} {item.relative_to(root)}")
107124

108125

109-
def copy_item(src: Path, dest: Path, exclude_patterns: List[str], root: Path) -> None:
126+
def copy_item(
127+
src: Path,
128+
dest: Path,
129+
exclude_patterns: List[str],
130+
root: Path,
131+
verbose: bool = False,
132+
) -> None:
110133
if is_excluded(src, exclude_patterns, root):
111-
print(f"🚫 Skipped (excluded): {src.relative_to(root)}")
134+
if verbose:
135+
print(f"🚫 Skipped (excluded): {src.relative_to(root)}")
112136
return
113137
if src.is_dir():
114-
copy_directory(src, dest, exclude_patterns, root)
138+
copy_directory(src, dest, exclude_patterns, root, verbose)
115139
else:
116-
copy_file(src, dest, root)
140+
copy_file(src, dest, root, verbose)
117141

118142

119143
def run_build(
120-
build_cfg: BuildConfig, config_dir: Path, out_override: Optional[str]
144+
build_cfg: BuildConfig,
145+
config_dir: Path,
146+
out_override: Optional[str],
147+
verbose: bool = False,
121148
) -> None:
122149
includes = build_cfg.get("include", [])
123150
excludes = build_cfg.get("exclude", [])
@@ -133,28 +160,36 @@ def run_build(
133160
assert src_pattern is not None, f"Missing required 'src' in entry: {entry_dict}"
134161

135162
if not src_pattern or src_pattern.strip() in {".", ""}:
136-
print(
137-
f"{YELLOW}⚠️ Skipping invalid include pattern: {src_pattern!r}{RESET}"
138-
)
163+
if verbose:
164+
print(
165+
f"{YELLOW}⚠️ Skipping invalid include pattern: "
166+
f"{src_pattern!r}{RESET}"
167+
)
139168
continue
140169

141170
dest_name = entry_dict.get("dest")
142-
matches = (
143-
list(config_dir.rglob(src_pattern))
144-
if "**" in src_pattern
145-
else list(config_dir.glob(src_pattern))
146-
)
171+
if Path(config_dir / src_pattern).is_dir():
172+
matches = [config_dir / src_pattern]
173+
else:
174+
matches = (
175+
list(config_dir.rglob(src_pattern))
176+
if "**" in src_pattern
177+
else list(config_dir.glob(src_pattern))
178+
)
179+
147180
if not matches:
148-
print(f"{YELLOW}⚠️ No matches for {src_pattern}{RESET}")
181+
if verbose:
182+
print(f"{YELLOW}⚠️ No matches for {src_pattern}{RESET}")
149183
continue
150184

151185
for src in matches:
152186
if not src.exists():
153-
print(f"{YELLOW}⚠️ Missing: {src}{RESET}")
187+
if verbose:
188+
print(f"{YELLOW}⚠️ Missing: {src}{RESET}")
154189
continue
155190

156191
dest: Path = out_dir / (dest_name or src.name)
157-
copy_item(src, dest, excludes, config_dir)
192+
copy_item(src, dest, excludes, config_dir, verbose)
158193

159194
print(f"✅ Build completed → {out_dir}\n")
160195

@@ -230,12 +265,21 @@ def main(argv: Optional[List[str]] = None) -> int:
230265
action="store_true",
231266
help="Show version information and exit",
232267
)
233-
parser.add_argument(
268+
269+
# Quiet and verbose cannot coexist
270+
noise_group = parser.add_mutually_exclusive_group()
271+
noise_group.add_argument(
234272
"-q",
235273
"--quiet",
236274
action="store_true",
237275
help="Suppress non-error output",
238276
)
277+
noise_group.add_argument(
278+
"-v",
279+
"--verbose",
280+
action="store_true",
281+
help="Show detailed logs for each file operation",
282+
)
239283
args = parser.parse_args(argv)
240284

241285
# --- Version flag ---
@@ -274,19 +318,21 @@ def main(argv: Optional[List[str]] = None) -> int:
274318
# everything printed inside this block is discarded
275319
with contextlib.redirect_stdout(buffer):
276320
for i, build_cfg in enumerate(builds, 1):
277-
run_build(build_cfg, config_dir, args.out)
321+
run_build(
322+
build_cfg, config_dir, args.out, verbose=args.verbose or False
323+
)
278324
# still return 0 to indicate success
279325
return 0
280326

281-
# --- Normal mode ---
327+
# --- Normal / verbose mode ---
282328
print(f"🔧 Using config: {config_path.name}")
283329
print(f"📁 Config base: {config_dir}")
284330
print(f"📂 Invoked from: {cwd}\n")
285331
print(f"🔧 Running {len(builds)} build(s)\n")
286332

287333
for i, build_cfg in enumerate(builds, 1):
288334
print(f"▶️ Build {i}/{len(builds)}")
289-
run_build(build_cfg, config_dir, args.out)
335+
run_build(build_cfg, config_dir, args.out, verbose=args.verbose or False)
290336

291337
print("🎉 All builds complete.")
292338
return 0

dev/make_script.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def strip_redundant_blocks(text: str) -> str:
142142

143143
OUT_FILE.parent.mkdir(parents=True, exist_ok=True)
144144
OUT_FILE.write_text(final_script, encoding="utf-8")
145+
OUT_FILE.touch()
145146

146147
print(
147148
f"✅ Built {OUT_FILE.relative_to(ROOT)} ({len(parts)} modules) — "

src/pocket_build/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env python3
2+
# src/pocket_build/__main__.py
23
import sys
34

45
from .cli import main

src/pocket_build/build.py

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# src/pocket_build/build.py
12
import shutil
23
from pathlib import Path
34
from typing import List, Optional
@@ -6,41 +7,58 @@
67
from .utils import GREEN, RESET, YELLOW, is_excluded
78

89

9-
def copy_file(src: Path, dest: Path, root: Path) -> None:
10+
def copy_file(src: Path, dest: Path, root: Path, verbose: bool = False) -> None:
1011
dest.parent.mkdir(parents=True, exist_ok=True)
1112
shutil.copy2(src, dest)
12-
print(f"📄 {src.relative_to(root)}{dest.relative_to(root)}")
13+
if verbose:
14+
print(f"📄 {src.relative_to(root)}{dest.relative_to(root)}")
1315

1416

1517
def copy_directory(
16-
src: Path, dest: Path, exclude_patterns: List[str], root: Path
18+
src: Path,
19+
dest: Path,
20+
exclude_patterns: List[str],
21+
root: Path,
22+
verbose: bool = False,
1723
) -> None:
1824
"""Recursively copy directory contents, skipping excluded files."""
1925
for item in src.rglob("*"):
2026
if is_excluded(item, exclude_patterns, root):
21-
print(f"🚫 Skipped: {item.relative_to(root)}")
27+
if verbose:
28+
print(f"🚫 Skipped: {item.relative_to(root)}")
2229
continue
2330
target = dest / item.relative_to(src)
2431
if item.is_dir():
2532
target.mkdir(parents=True, exist_ok=True)
2633
else:
2734
target.parent.mkdir(parents=True, exist_ok=True)
2835
shutil.copy2(item, target)
29-
print(f"{GREEN}📄{RESET} {item.relative_to(root)}")
36+
if verbose:
37+
print(f"{GREEN}📄{RESET} {item.relative_to(root)}")
3038

3139

32-
def copy_item(src: Path, dest: Path, exclude_patterns: List[str], root: Path) -> None:
40+
def copy_item(
41+
src: Path,
42+
dest: Path,
43+
exclude_patterns: List[str],
44+
root: Path,
45+
verbose: bool = False,
46+
) -> None:
3347
if is_excluded(src, exclude_patterns, root):
34-
print(f"🚫 Skipped (excluded): {src.relative_to(root)}")
48+
if verbose:
49+
print(f"🚫 Skipped (excluded): {src.relative_to(root)}")
3550
return
3651
if src.is_dir():
37-
copy_directory(src, dest, exclude_patterns, root)
52+
copy_directory(src, dest, exclude_patterns, root, verbose)
3853
else:
39-
copy_file(src, dest, root)
54+
copy_file(src, dest, root, verbose)
4055

4156

4257
def run_build(
43-
build_cfg: BuildConfig, config_dir: Path, out_override: Optional[str]
58+
build_cfg: BuildConfig,
59+
config_dir: Path,
60+
out_override: Optional[str],
61+
verbose: bool = False,
4462
) -> None:
4563
includes = build_cfg.get("include", [])
4664
excludes = build_cfg.get("exclude", [])
@@ -56,27 +74,35 @@ def run_build(
5674
assert src_pattern is not None, f"Missing required 'src' in entry: {entry_dict}"
5775

5876
if not src_pattern or src_pattern.strip() in {".", ""}:
59-
print(
60-
f"{YELLOW}⚠️ Skipping invalid include pattern: {src_pattern!r}{RESET}"
61-
)
77+
if verbose:
78+
print(
79+
f"{YELLOW}⚠️ Skipping invalid include pattern: "
80+
f"{src_pattern!r}{RESET}"
81+
)
6282
continue
6383

6484
dest_name = entry_dict.get("dest")
65-
matches = (
66-
list(config_dir.rglob(src_pattern))
67-
if "**" in src_pattern
68-
else list(config_dir.glob(src_pattern))
69-
)
85+
if Path(config_dir / src_pattern).is_dir():
86+
matches = [config_dir / src_pattern]
87+
else:
88+
matches = (
89+
list(config_dir.rglob(src_pattern))
90+
if "**" in src_pattern
91+
else list(config_dir.glob(src_pattern))
92+
)
93+
7094
if not matches:
71-
print(f"{YELLOW}⚠️ No matches for {src_pattern}{RESET}")
95+
if verbose:
96+
print(f"{YELLOW}⚠️ No matches for {src_pattern}{RESET}")
7297
continue
7398

7499
for src in matches:
75100
if not src.exists():
76-
print(f"{YELLOW}⚠️ Missing: {src}{RESET}")
101+
if verbose:
102+
print(f"{YELLOW}⚠️ Missing: {src}{RESET}")
77103
continue
78104

79105
dest: Path = out_dir / (dest_name or src.name)
80-
copy_item(src, dest, excludes, config_dir)
106+
copy_item(src, dest, excludes, config_dir, verbose)
81107

82108
print(f"✅ Build completed → {out_dir}\n")

src/pocket_build/cli.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,21 @@ def main(argv: Optional[List[str]] = None) -> int:
8282
action="store_true",
8383
help="Show version information and exit",
8484
)
85-
parser.add_argument(
85+
86+
# Quiet and verbose cannot coexist
87+
noise_group = parser.add_mutually_exclusive_group()
88+
noise_group.add_argument(
8689
"-q",
8790
"--quiet",
8891
action="store_true",
8992
help="Suppress non-error output",
9093
)
94+
noise_group.add_argument(
95+
"-v",
96+
"--verbose",
97+
action="store_true",
98+
help="Show detailed logs for each file operation",
99+
)
91100
args = parser.parse_args(argv)
92101

93102
# --- Version flag ---
@@ -126,19 +135,21 @@ def main(argv: Optional[List[str]] = None) -> int:
126135
# everything printed inside this block is discarded
127136
with contextlib.redirect_stdout(buffer):
128137
for i, build_cfg in enumerate(builds, 1):
129-
run_build(build_cfg, config_dir, args.out)
138+
run_build(
139+
build_cfg, config_dir, args.out, verbose=args.verbose or False
140+
)
130141
# still return 0 to indicate success
131142
return 0
132143

133-
# --- Normal mode ---
144+
# --- Normal / verbose mode ---
134145
print(f"🔧 Using config: {config_path.name}")
135146
print(f"📁 Config base: {config_dir}")
136147
print(f"📂 Invoked from: {cwd}\n")
137148
print(f"🔧 Running {len(builds)} build(s)\n")
138149

139150
for i, build_cfg in enumerate(builds, 1):
140151
print(f"▶️ Build {i}/{len(builds)}")
141-
run_build(build_cfg, config_dir, args.out)
152+
run_build(build_cfg, config_dir, args.out, verbose=args.verbose or False)
142153

143154
print("🎉 All builds complete.")
144155
return 0

0 commit comments

Comments
 (0)