Skip to content

Commit 3848b2a

Browse files
author
Kazys Stepanas
committed
Add support for controlling the number of jobs used by some cmake tasks
- Add support for `build` arg `--cmake-jobs` to set the number of jobs to use. - Rename `_get_make_arguments()` to `_get_make_jobs_arguments()` for clarity
1 parent 6875f1d commit 3848b2a

2 files changed

Lines changed: 68 additions & 28 deletions

File tree

colcon_cmake/task/cmake/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,27 @@ def get_generator(path, cmake_args=None):
139139
return generator
140140

141141

142+
def is_jobs_base_generator(path, cmake_args=None):
143+
"""
144+
Check if the used CMake generator supports jobs base arguments (-jN).
145+
146+
:param str path: The path of the directory contain the CMake cache file
147+
:param list cmake_args: The CMake command line arguments
148+
:rtype: bool
149+
"""
150+
known_jobs_base_multi_configuration_generators = (
151+
'Ninja Multi-Config',
152+
)
153+
if not is_multi_configuration_generator(path, cmake_args):
154+
# Historically we assume any non-multi-config generator is jobs based.
155+
return True
156+
generator = get_generator(path, cmake_args)
157+
for multi in known_jobs_base_multi_configuration_generators:
158+
if multi in generator:
159+
return True
160+
return False
161+
162+
142163
def is_multi_configuration_generator(path, cmake_args=None):
143164
"""
144165
Check if the used CMake generator is a multi configuration generator.

colcon_cmake/task/cmake/build.py

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from colcon_cmake.task.cmake import get_variable_from_cmake_cache
1515
from colcon_cmake.task.cmake import get_visual_studio_version
1616
from colcon_cmake.task.cmake import has_target
17+
from colcon_cmake.task.cmake import is_jobs_base_generator
1718
from colcon_cmake.task.cmake import is_multi_configuration_generator
1819
from colcon_core.environment import create_environment_scripts
1920
from colcon_core.logging import colcon_logger
@@ -63,6 +64,12 @@ def add_arguments(self, *, parser): # noqa: D102
6364
'--cmake-force-configure',
6465
action='store_true',
6566
help='Force CMake configure step')
67+
parser.add_argument(
68+
'--cmake-jobs',
69+
type=int,
70+
help='Number of jobs to use for supported generators (e.g., Ninja '
71+
'Makefiles). Negative values subtract from the maximum '
72+
'available, so --jobs=-1 uses all bar 1 available threads.')
6673

6774
async def build( # noqa: D102
6875
self, *, additional_hooks=None, skip_hook_creation=False,
@@ -221,6 +228,8 @@ async def _build(self, args, env, *, additional_targets=None):
221228
if additional_targets:
222229
targets += additional_targets
223230

231+
jobs_base_generator = is_jobs_base_generator(
232+
args.build_base, args.cmake_args)
224233
multi_configuration_generator = is_multi_configuration_generator(
225234
args.build_base, args.cmake_args)
226235
if multi_configuration_generator:
@@ -240,8 +249,8 @@ async def _build(self, args, env, *, additional_targets=None):
240249
cmd += ['--clean-first']
241250
if multi_configuration_generator:
242251
cmd += ['--config', self._get_configuration(args)]
243-
else:
244-
job_args = self._get_make_arguments(env)
252+
if jobs_base_generator:
253+
job_args = self._get_make_jobs_arguments(args, env)
245254
if job_args:
246255
cmd += ['--'] + job_args
247256
completed = await run(
@@ -281,7 +290,7 @@ def _get_msbuild_environment(self, args, env):
281290
env['CL'] = ' '.join(cl_split)
282291
return env
283292

284-
def _get_make_arguments(self, env):
293+
def _get_make_jobs_arguments(self, args, env):
285294
"""
286295
Get the make arguments to limit the number of simultaneously run jobs.
287296
@@ -291,28 +300,37 @@ def _get_make_arguments(self, env):
291300
:returns: list of make arguments
292301
:rtype: list of strings
293302
"""
294-
# check MAKEFLAGS for -j/--jobs/-l/--load-average arguments
295-
makeflags = env.get('MAKEFLAGS', '')
296-
regex = (
297-
r'(?:^|\s)'
298-
r'(-?(?:j|l)(?:\s*[0-9]+|\s|$))'
299-
r'|'
300-
r'(?:^|\s)'
301-
r'((?:--)?(?:jobs|load-average)(?:(?:=|\s+)[0-9]+|(?:\s|$)))'
302-
)
303-
matches = re.findall(regex, makeflags) or []
304-
matches = [m[0] or m[1] for m in matches]
305-
if matches:
306-
# do not extend make arguments, let MAKEFLAGS set things
307-
return []
308-
# Use the number of CPU cores
309-
jobs = os.cpu_count()
310-
with suppress(AttributeError):
311-
# consider restricted set of CPUs if applicable
312-
jobs = min(jobs, len(os.sched_getaffinity(0)))
313-
if jobs is None:
314-
# the number of cores can't be determined
315-
return []
303+
generator = get_generator(args.build_base)
304+
if "Makefiles" in generator and args.cmake_jobs is None:
305+
# check MAKEFLAGS for -j/--jobs/-l/--load-average arguments
306+
# Note: Ninja does not support environment variables.
307+
makeflags = env.get('MAKEFLAGS', '')
308+
regex = (
309+
r'(?:^|\s)'
310+
r'(-?(?:j|l)(?:\s*[0-9]+|\s|$))'
311+
r'|'
312+
r'(?:^|\s)'
313+
r'((?:--)?(?:jobs|load-average)(?:(?:=|\s+)[0-9]+|(?:\s|$)))'
314+
)
315+
matches = re.findall(regex, makeflags) or []
316+
matches = [m[0] or m[1] for m in matches]
317+
if matches:
318+
# do not extend make arguments, let MAKEFLAGS set things
319+
return []
320+
# Use command line specified jobs if positive.
321+
jobs_args = args.cmake_jobs if args.cmake_jobs is not None else 0
322+
jobs = 0
323+
if jobs_args <= 0:
324+
# Base off the number of CPU cores if jobs arg non-positive.
325+
jobs = os.cpu_count()
326+
with suppress(AttributeError):
327+
# consider restricted set of CPUs if applicable
328+
jobs = min(jobs, len(os.sched_getaffinity(0)))
329+
if jobs is None:
330+
# the number of cores can't be determined
331+
return []
332+
# Finalise jobs as as CPU count deducting the limit specified.
333+
jobs = max(jobs + jobs_args, 1)
316334
return [
317335
'-j{jobs}'.format_map(locals()),
318336
'-l{jobs}'.format_map(locals()),
@@ -325,7 +343,8 @@ async def _install(self, args, env):
325343
raise RuntimeError("Could not find 'cmake' executable")
326344
cmd = [CMAKE_EXECUTABLE]
327345
cmake_ver = get_cmake_version()
328-
allow_job_args = True
346+
allow_job_args = is_jobs_base_generator(
347+
args.build_base, args.cmake_args)
329348
if cmake_ver and cmake_ver >= parse_version('3.15.0'):
330349
# CMake 3.15+ supports invoking `cmake --install`
331350
cmd += ['--install', args.build_base]
@@ -343,8 +362,8 @@ async def _install(self, args, env):
343362
args.build_base, args.cmake_args)
344363
if multi_configuration_generator:
345364
cmd += ['--config', self._get_configuration(args)]
346-
elif allow_job_args:
347-
job_args = self._get_make_arguments(env)
365+
if allow_job_args:
366+
job_args = self._get_make_jobs_arguments(args, env)
348367
if job_args:
349368
cmd += ['--'] + job_args
350369
return await run(

0 commit comments

Comments
 (0)