88import re
99import shlex
1010import subprocess
11+ import sys
1112import textwrap
1213import urllib .request
1314from pathlib import Path
1617import yaml
1718from west .commands import WestCommand
1819
20+ sys .path .append (os .fspath (Path (__file__ ).parent .parent ))
21+ import zephyr_module
22+ from zephyr_ext_common import ZEPHYR_BASE
23+
1924try :
2025 from yaml import CSafeDumper as SafeDumper
2126 from yaml import CSafeLoader as SafeLoader
2934WEST_PATCH_BASE = Path ("zephyr" ) / "patches"
3035WEST_PATCH_YAML = Path ("zephyr" ) / "patches.yml"
3136
32- _WEST_MANIFEST_DIR = Path ("WEST_MANIFEST_DIR" )
33- _WEST_TOPDIR = Path ("WEST_TOPDIR" )
34-
3537
3638class Patch (WestCommand ):
3739 def __init__ (self ):
@@ -101,38 +103,50 @@ def do_add_parser(self, parser_adder):
101103 parser .add_argument (
102104 "-b" ,
103105 "--patch-base" ,
104- help = "Directory containing patch files" ,
106+ help = f"""
107+ Directory containing patch files (absolute or relative to module dir,
108+ default: { WEST_PATCH_BASE } )""" ,
105109 metavar = "DIR" ,
106- default = _WEST_MANIFEST_DIR / WEST_PATCH_BASE ,
107110 type = Path ,
108111 )
109112 parser .add_argument (
110113 "-l" ,
111114 "--patch-yml" ,
112- help = "Path to patches.yml file" ,
115+ help = f"""
116+ Path to patches.yml file (absolute or relative to module dir,
117+ default: { WEST_PATCH_YAML } )""" ,
113118 metavar = "FILE" ,
114- default = _WEST_MANIFEST_DIR / WEST_PATCH_YAML ,
115119 type = Path ,
116120 )
117121 parser .add_argument (
118122 "-w" ,
119123 "--west-workspace" ,
120124 help = "West workspace" ,
121125 metavar = "DIR" ,
122- default = _WEST_TOPDIR ,
123126 type = Path ,
124127 )
125128 parser .add_argument (
126- "-m" ,
127- "--module" ,
129+ "-sm" ,
130+ "--src-module" ,
131+ dest = "src_module" ,
132+ metavar = "MODULE" ,
133+ type = str ,
134+ help = """
135+ Zephyr module containing the patch definition (name, absolute path or
136+ path relative to west-workspace)""" ,
137+ )
138+ parser .add_argument (
139+ "-dm" ,
140+ "--dst-module" ,
128141 action = "append" ,
129- dest = "modules" ,
130- metavar = "DIR" ,
131- type = Path ,
132- help = "Zephyr module directory to run the 'patch' command for. "
133- "Option can be passed multiple times. "
134- "If this option is not given, the 'patch' command will run for Zephyr "
135- "and all modules." ,
142+ dest = "dst_modules" ,
143+ metavar = "MODULE" ,
144+ type = str ,
145+ help = """
146+ Zephyr module to run the 'patch' command for.
147+ Option can be passed multiple times.
148+ If this option is not given, the 'patch' command will run for Zephyr
149+ and all modules.""" ,
136150 )
137151
138152 subparsers = parser .add_subparsers (
@@ -257,14 +271,36 @@ def filter_args(self, args):
257271 self .die ("could not retrieve manifest path from west configuration" )
258272
259273 topdir = Path (self .topdir )
260- manifest_dir = topdir / manifest_path
261274
262- if args .patch_base .is_relative_to (_WEST_MANIFEST_DIR ):
263- args .patch_base = manifest_dir / args .patch_base .relative_to (_WEST_MANIFEST_DIR )
264- if args .patch_yml .is_relative_to (_WEST_MANIFEST_DIR ):
265- args .patch_yml = manifest_dir / args .patch_yml .relative_to (_WEST_MANIFEST_DIR )
266- if args .west_workspace .is_relative_to (_WEST_TOPDIR ):
267- args .west_workspace = topdir / args .west_workspace .relative_to (_WEST_TOPDIR )
275+ if args .src_module is not None :
276+ mod_path = self .get_module_path (args .src_module )
277+ if mod_path is None :
278+ self .die (f'Source module "{ args .src_module } " not found' )
279+ if args .patch_base is not None and args .patch_base .is_absolute ():
280+ self .die ("patch-base must not be an absolute path in combination with src-module" )
281+ if args .patch_yml is not None and args .patch_yml .is_absolute ():
282+ self .die ("patch-yml must not be an absolute path in combination with src-module" )
283+ manifest_dir = topdir / mod_path
284+ else :
285+ manifest_dir = topdir / manifest_path
286+
287+ if args .patch_base is None :
288+ args .patch_base = manifest_dir / WEST_PATCH_BASE
289+ if not args .patch_base .is_absolute ():
290+ args .patch_base = manifest_dir / args .patch_base
291+
292+ if args .patch_yml is None :
293+ args .patch_yml = manifest_dir / WEST_PATCH_YAML
294+ elif not args .patch_yml .is_absolute ():
295+ args .patch_yml = manifest_dir / args .patch_yml
296+
297+ if args .west_workspace is None :
298+ args .west_workspace = topdir
299+ elif not args .west_workspace .is_absolute ():
300+ args .west_workspace = topdir / args .west_workspace
301+
302+ if args .dst_modules is not None :
303+ args .dst_modules = [self .get_module_path (m ) for m in args .dst_modules ]
268304
269305 def load_yml (self , args , allow_missing ):
270306 if not os .path .isfile (args .patch_yml ):
@@ -303,9 +339,9 @@ def do_run(self, args, _):
303339 "gh-fetch" : self .gh_fetch ,
304340 }
305341
306- method [args .subcommand ](args , yml , args .modules )
342+ method [args .subcommand ](args , yml , args .dst_modules )
307343
308- def apply (self , args , yml , mods = None ):
344+ def apply (self , args , yml , dst_mods = None ):
309345 patches = yml .get ("patches" , [])
310346 if not patches :
311347 return
@@ -315,8 +351,11 @@ def apply(self, args, yml, mods=None):
315351 patched_mods = set ()
316352
317353 for patch_info in patches :
318- mod = Path (patch_info ["module" ])
319- if mods and mod not in mods :
354+ mod = self .get_module_path (patch_info ["module" ])
355+ if mod is None :
356+ continue
357+
358+ if dst_mods and mod not in dst_mods :
320359 continue
321360
322361 pth = patch_info ["path" ]
@@ -377,7 +416,7 @@ def apply(self, args, yml, mods=None):
377416
378417 self .die (f"failed to apply patch { failed_patch } " )
379418
380- def clean (self , args , yml , mods = None ):
419+ def clean (self , args , yml , dst_mods = None ):
381420 clean_cmd = yml ["clean-command" ]
382421 checkout_cmd = yml ["checkout-command" ]
383422
@@ -388,9 +427,14 @@ def clean(self, args, yml, mods=None):
388427 clean_cmd_list = shlex .split (clean_cmd )
389428 checkout_cmd_list = shlex .split (checkout_cmd )
390429
391- for mod , mod_path in Patch .get_mod_paths (args , yml ).items ():
392- if mods and mod not in mods :
430+ for mod in yml .get ("patches" , []):
431+ m = self .get_module_path (mod .get ("module" ))
432+ if m is None :
433+ continue
434+ if dst_mods and m not in dst_mods :
393435 continue
436+ mod_path = Path (args .west_workspace ) / m
437+
394438 try :
395439 if checkout_cmd :
396440 self .dbg (f"Running '{ checkout_cmd } ' in { mod } .. " , end = "" )
@@ -414,13 +458,13 @@ def clean(self, args, yml, mods=None):
414458 # If this fails for some reason, just log it and continue
415459 self .err (f"failed to clean up { mod } : { e } " )
416460
417- def list (self , args , yml , mods = None ):
461+ def list (self , args , yml , dst_mods = None ):
418462 patches = yml .get ("patches" , [])
419463 if not patches :
420464 return
421465
422466 for patch_info in patches :
423- if mods and Path (patch_info ["module" ]) not in mods :
467+ if dst_mods and self . get_module_path (patch_info ["module" ]) not in dst_mods :
424468 continue
425469 self .inf (patch_info )
426470
@@ -490,16 +534,23 @@ def get_file_sha256sum(filename: Path) -> str:
490534
491535 return digest .hexdigest ()
492536
493- @staticmethod
494- def get_mod_paths (args , yml ):
495- patches = yml .get ("patches" , [])
496- if not patches :
497- return {}
537+ def get_module_path (self , module_name_or_path ):
538+ if module_name_or_path is None :
539+ return None
498540
499- mod_paths = {}
500- for patch_info in patches :
501- mod = Path (patch_info ["module" ])
502- mod_path = os .path .realpath (Path (args .west_workspace ) / mod )
503- mod_paths [mod ] = mod_path
541+ topdir = Path (self .topdir )
542+
543+ if Path (module_name_or_path ).is_absolute ():
544+ if Path (module_name_or_path ).is_dir ():
545+ return Path (module_name_or_path ).resolve ().relative_to (topdir )
546+ return None
547+
548+ if (topdir / module_name_or_path ).is_dir ():
549+ return Path (module_name_or_path )
550+
551+ all_modules = zephyr_module .parse_modules (ZEPHYR_BASE , self .manifest )
552+ for m in all_modules :
553+ if m .meta ['name' ] == module_name_or_path :
554+ return Path (m .project ).relative_to (topdir )
504555
505- return mod_paths
556+ return None
0 commit comments