1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5ProcessorCount
6--------------
7
8ProcessorCount(var)
9
10Determine the number of processors/cores and save value in ${var}
11
12Sets the variable named ${var} to the number of physical cores
13available on the machine if the information can be determined.
14Otherwise it is set to 0.  Currently this functionality is implemented
15for AIX, cygwin, FreeBSD, HPUX, Linux, macOS, QNX, Sun and
16Windows.
17
18.. versionchanged:: 3.15
19  On Linux, returns the container CPU count instead of the host CPU count.
20
21This function is guaranteed to return a positive integer (>=1) if it
22succeeds.  It returns 0 if there's a problem determining the processor
23count.
24
25Example use, in a ctest -S dashboard script:
26
27::
28
29   include(ProcessorCount)
30   ProcessorCount(N)
31   if(NOT N EQUAL 0)
32     set(CTEST_BUILD_FLAGS -j${N})
33     set(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${N})
34   endif()
35
36
37
38This function is intended to offer an approximation of the value of
39the number of compute cores available on the current machine, such
40that you may use that value for parallel building and parallel
41testing.  It is meant to help utilize as much of the machine as seems
42reasonable.  Of course, knowledge of what else might be running on the
43machine simultaneously should be used when deciding whether to request
44a machine's full capacity all for yourself.
45#]=======================================================================]
46
47# A more reliable way might be to compile a small C program that uses the CPUID
48# instruction, but that again requires compiler support or compiling assembler
49# code.
50
51function(ProcessorCount var)
52  # Unknown:
53  set(count 0)
54
55  if(WIN32)
56    # Windows:
57    set(count "$ENV{NUMBER_OF_PROCESSORS}")
58    #message("ProcessorCount: WIN32, trying environment variable")
59  endif()
60
61  if(NOT count)
62    # Mac, FreeBSD, OpenBSD (systems with sysctl):
63    find_program(ProcessorCount_cmd_sysctl sysctl
64      PATHS /usr/sbin /sbin)
65    mark_as_advanced(ProcessorCount_cmd_sysctl)
66    if(ProcessorCount_cmd_sysctl)
67      execute_process(COMMAND ${ProcessorCount_cmd_sysctl} -n hw.ncpu
68        ERROR_QUIET
69        OUTPUT_STRIP_TRAILING_WHITESPACE
70        OUTPUT_VARIABLE count)
71      #message("ProcessorCount: trying sysctl '${ProcessorCount_cmd_sysctl}'")
72    endif()
73  endif()
74
75  if(NOT count)
76    # Linux (systems with nproc):
77    # Prefer nproc to getconf if available as getconf may return the host CPU count in Linux containers
78    find_program(ProcessorCount_cmd_nproc nproc)
79    mark_as_advanced(ProcessorCount_cmd_nproc)
80    if(ProcessorCount_cmd_nproc)
81      execute_process(COMMAND ${ProcessorCount_cmd_nproc}
82        ERROR_QUIET
83        OUTPUT_STRIP_TRAILING_WHITESPACE
84        OUTPUT_VARIABLE count)
85      #message("ProcessorCount: trying nproc '${ProcessorCount_cmd_nproc}'")
86    endif()
87  endif()
88
89  if(NOT count)
90    # Linux (systems with getconf):
91    find_program(ProcessorCount_cmd_getconf getconf)
92    mark_as_advanced(ProcessorCount_cmd_getconf)
93    if(ProcessorCount_cmd_getconf)
94      execute_process(COMMAND ${ProcessorCount_cmd_getconf} _NPROCESSORS_ONLN
95        ERROR_QUIET
96        OUTPUT_STRIP_TRAILING_WHITESPACE
97        OUTPUT_VARIABLE count)
98      #message("ProcessorCount: trying getconf '${ProcessorCount_cmd_getconf}'")
99    endif()
100  endif()
101
102  if(NOT count)
103    # HPUX (systems with machinfo):
104    find_program(ProcessorCount_cmd_machinfo machinfo
105      PATHS /usr/contrib/bin)
106    mark_as_advanced(ProcessorCount_cmd_machinfo)
107    if(ProcessorCount_cmd_machinfo)
108      execute_process(COMMAND ${ProcessorCount_cmd_machinfo}
109        ERROR_QUIET
110        OUTPUT_STRIP_TRAILING_WHITESPACE
111        OUTPUT_VARIABLE machinfo_output)
112      string(REGEX MATCHALL "Number of CPUs = ([0-9]+)" procs "${machinfo_output}")
113      set(count "${CMAKE_MATCH_1}")
114      if(NOT count)
115        string(REGEX MATCHALL "([0-9]+) logical processors" procs "${machinfo_output}")
116        set(count "${CMAKE_MATCH_1}")
117      endif()
118      #message("ProcessorCount: trying machinfo '${ProcessorCount_cmd_machinfo}'")
119    else()
120      find_program(ProcessorCount_cmd_mpsched mpsched)
121      mark_as_advanced(ProcessorCount_cmd_mpsched)
122      if(ProcessorCount_cmd_mpsched)
123        execute_process(COMMAND ${ProcessorCount_cmd_mpsched} -s
124          OUTPUT_QUIET
125          ERROR_STRIP_TRAILING_WHITESPACE
126          ERROR_VARIABLE mpsched_output)
127        string(REGEX MATCHALL "Processor Count *: *([0-9]+)" procs "${mpsched_output}")
128        set(count "${CMAKE_MATCH_1}")
129        #message("ProcessorCount: trying mpsched -s '${ProcessorCount_cmd_mpsched}'")
130      endif()
131    endif()
132  endif()
133
134  if(NOT count)
135    # AIX (systems with lsconf):
136    find_program(ProcessorCount_cmd_lsconf lsconf
137      PATHS /usr/sbin)
138    mark_as_advanced(ProcessorCount_cmd_lsconf)
139    if(ProcessorCount_cmd_lsconf)
140      execute_process(COMMAND ${ProcessorCount_cmd_lsconf}
141        ERROR_QUIET
142        OUTPUT_STRIP_TRAILING_WHITESPACE
143        OUTPUT_VARIABLE lsconf_output)
144      string(REGEX MATCHALL "Number Of Processors: ([0-9]+)" procs "${lsconf_output}")
145      set(count "${CMAKE_MATCH_1}")
146      #message("ProcessorCount: trying lsconf '${ProcessorCount_cmd_lsconf}'")
147    endif()
148  endif()
149
150  if(NOT count)
151    # QNX (systems with pidin):
152    find_program(ProcessorCount_cmd_pidin pidin)
153    mark_as_advanced(ProcessorCount_cmd_pidin)
154    if(ProcessorCount_cmd_pidin)
155      execute_process(COMMAND ${ProcessorCount_cmd_pidin} info
156        ERROR_QUIET
157        OUTPUT_STRIP_TRAILING_WHITESPACE
158        OUTPUT_VARIABLE pidin_output)
159      string(REGEX MATCHALL "Processor[0-9]+: " procs "${pidin_output}")
160      list(LENGTH procs count)
161      #message("ProcessorCount: trying pidin '${ProcessorCount_cmd_pidin}'")
162    endif()
163  endif()
164
165  if(NOT count)
166    # Sun (systems where psrinfo tool is available)
167    find_program(ProcessorCount_cmd_psrinfo psrinfo PATHS /usr/sbin /sbin)
168    mark_as_advanced(ProcessorCount_cmd_psrinfo)
169    if (ProcessorCount_cmd_psrinfo)
170      execute_process(COMMAND ${ProcessorCount_cmd_psrinfo} -p -v
171        ERROR_QUIET
172        OUTPUT_STRIP_TRAILING_WHITESPACE
173        OUTPUT_VARIABLE psrinfo_output)
174      string(REGEX MATCHALL "has [0-9]+ virtual processor" procs "${psrinfo_output}")
175      set(count "")
176      foreach(proc ${procs})
177        string(REGEX MATCH "has ([0-9]+) virtual" res ${proc})
178        math(EXPR count "${count} + ${CMAKE_MATCH_1}")
179      endforeach()
180      #message("ProcessorCount: trying '${ProcessorCount_cmd_psrinfo}' -p -v")
181    else()
182      # Sun (systems where uname -X emits "NumCPU" in its output):
183      find_program(ProcessorCount_cmd_uname uname)
184      mark_as_advanced(ProcessorCount_cmd_uname)
185      if(ProcessorCount_cmd_uname)
186        execute_process(COMMAND ${ProcessorCount_cmd_uname} -X
187          ERROR_QUIET
188          OUTPUT_STRIP_TRAILING_WHITESPACE
189          OUTPUT_VARIABLE uname_X_output)
190        string(REGEX MATCHALL "NumCPU = ([0-9]+)" procs "${uname_X_output}")
191        set(count "${CMAKE_MATCH_1}")
192        #message("ProcessorCount: trying uname -X '${ProcessorCount_cmd_uname}'")
193      endif()
194    endif()
195  endif()
196
197  # Execute this code when all previously attempted methods return empty
198  # output:
199  #
200  if(NOT count)
201    # Systems with /proc/cpuinfo:
202    set(cpuinfo_file /proc/cpuinfo)
203    if(EXISTS "${cpuinfo_file}")
204      file(STRINGS "${cpuinfo_file}" procs REGEX "^processor.: [0-9]+$")
205      list(LENGTH procs count)
206      #message("ProcessorCount: trying cpuinfo '${cpuinfo_file}'")
207    endif()
208  endif()
209
210  if(NOT count)
211    # Haiku
212    find_program(ProcessorCount_cmd_sysinfo sysinfo)
213    if(ProcessorCount_cmd_sysinfo)
214      execute_process(COMMAND ${ProcessorCount_cmd_sysinfo}
215        ERROR_QUIET
216        OUTPUT_STRIP_TRAILING_WHITESPACE
217        OUTPUT_VARIABLE sysinfo_X_output)
218      string(REGEX MATCHALL "\nCPU #[0-9]+:" procs "\n${sysinfo_X_output}")
219      list(LENGTH procs count)
220      #message("ProcessorCount: trying sysinfo '${ProcessorCount_cmd_sysinfo}'")
221    endif()
222  endif()
223
224  # Since cygwin builds of CMake do not define WIN32 anymore, but they still
225  # run on Windows, and will still have this env var defined:
226  #
227  if(NOT count)
228    set(count "$ENV{NUMBER_OF_PROCESSORS}")
229    #message("ProcessorCount: last fallback, trying environment variable")
230  endif()
231
232  # Ensure an integer return (avoid inadvertently returning an empty string
233  # or an error string)... If it's not a decimal integer, return 0:
234  #
235  if(NOT count MATCHES "^[0-9]+$")
236    set(count 0)
237  endif()
238
239  set(${var} ${count} PARENT_SCOPE)
240endfunction()
241