Skip to content

Commit e120eb3

Browse files
committed
app: Update Forall command to allow multiple concurrent processes
Demonstrate asynchronous behavior for the Forall command and add an argument to select the number of jobs. Signed-off-by: Pieter De Gendt <[email protected]>
1 parent c3aadf5 commit e120eb3

File tree

1 file changed

+27
-12
lines changed

1 file changed

+27
-12
lines changed

src/west/app/project.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
'''West project commands'''
77

88
import argparse
9+
import asyncio
910
from functools import partial
1011
import logging
1112
import os
@@ -1670,16 +1671,15 @@ def do_add_parser(self, parser_adder):
16701671
parser.add_argument('projects', metavar='PROJECT', nargs='*',
16711672
help='''projects (by name or path) to operate on;
16721673
defaults to active cloned projects''')
1674+
parser.add_argument('-j', '--jobs', nargs='?', const=-1,
1675+
default=1, type=int, action='store',
1676+
help='''Use multiple jobs to parallelize commands.
1677+
Pass no number or -1 to run commands on all cores.''')
16731678
return parser
16741679

1675-
def do_run(self, args, user_args):
1676-
failed = []
1677-
group_set = set(args.groups)
1678-
env = os.environ.copy()
1679-
for project in self._cloned_projects(args, only_active=not args.all):
1680-
if group_set and not group_set.intersection(set(project.groups)):
1681-
continue
1682-
1680+
async def run_for_project(self, project, args, sem):
1681+
async with sem:
1682+
env = os.environ.copy()
16831683
env["WEST_PROJECT_NAME"] = project.name
16841684
env["WEST_PROJECT_PATH"] = project.path
16851685
env["WEST_PROJECT_ABSPATH"] = project.abspath if project.abspath else ''
@@ -1689,10 +1689,25 @@ def do_run(self, args, user_args):
16891689

16901690
cwd = args.cwd if args.cwd else project.abspath
16911691

1692-
self.banner(
1693-
f'running "{args.subcommand}" in {project.name_and_path}:')
1694-
rc = subprocess.Popen(args.subcommand, shell=True, env=env,
1695-
cwd=cwd).wait()
1692+
self.banner(f'running "{args.subcommand}" in {project.name_and_path}:')
1693+
proc = await asyncio.create_subprocess_shell(args.subcommand,
1694+
cwd=cwd, env=env, shell=True)
1695+
return await proc.wait()
1696+
1697+
def do_run(self, args, unknown):
1698+
asyncio.run(self.do_run_async(args, unknown))
1699+
1700+
async def do_run_async(self, args, user_args):
1701+
sem = asyncio.Semaphore(args.jobs if args.jobs > 0 else os.cpu_count() or sys.maxsize)
1702+
1703+
group_set = set(args.groups)
1704+
projects = [p for p in self._cloned_projects(args, only_active=not args.all)
1705+
if not group_set or group_set.intersection(set(p.groups))]
1706+
1707+
rcs = await asyncio.gather(*[self.run_for_project(p, args, sem) for p in projects])
1708+
1709+
failed = []
1710+
for (project, rc) in zip(projects, rcs):
16961711
if rc:
16971712
failed.append(project)
16981713
self._handle_failed(args, failed)

0 commit comments

Comments
 (0)