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"""
1111Pocket Build — a tiny build system that fits in your pocket.
1212This single-file version is auto-generated from modular sources.
1313Version: 0.1.0
14- Commit: f7e8c12
14+ Commit: 2437a89
1515"""
1616
1717from __future__ import annotations
3030
3131from typing_extensions import NotRequired
3232
33-
3433# === types.py ===
34+ # src/pocket_build/types.py
35+
36+
3537class 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)
5256GREEN = "\033 [92m"
5357YELLOW = "\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+
7885def 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
92103def 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
119143def 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
0 commit comments