Repository: conan-io/conan

Issue:
[question] MsBuild tools.microsoft.msbuild:max_cpu_count
Hello,

we are using conan in CI/CD and we were wondering if we can utilize all of the CPUs in a MsBuild build when calling "conan build .".
We already know that we can mimic /maxcpucount:2 with tools.microsoft.msbuild:max_cpu_count=2 in our default.conf file, but how can we set /maxcpucount without any arguments? Because setting /maxcpucount  would use all available CPUs by default... "If you include the -maxcpucount switch without specifying a value, MSBuild will use up to the number of processors on the computer." [(source)](https://learn.microsoft.com/en-us/visualstudio/msbuild/building-multiple-projects-in-parallel-with-msbuild?view=vs-2022).

Another question: is there a better way to set tools.microsoft.msbuild:max_cpu_count=2 in CI/CD besides manually appending it to the default.conf?

Thanks in advance!

### Have you read the CONTRIBUTING guide?

- [ ] I've read the CONTRIBUTING guide


Discussion:
Hi @fellinga 

Thanks for your question.

> We already know that we can mimic /maxcpucount:2 with tools.microsoft.msbuild:max_cpu_count=2 in our default.conf file, but how can we set /maxcpucount without any arguments? 

It is not clear if you are using MSBuild (VS native project) or via CMake. In any case, note that you can also use the ``tools.build:jobs`` conf, and it will define the ``<ProcessorNumber>{}</ProcessorNumber>`` in ``MSBuildToolchain`` or to ``/MP`` flags in ``CMakeToolchain``. This is equally or even more important for multi-processor compilation parallelism than ``max_cpu_count``.

> Because setting /maxcpucount would use all available CPUs by default.

Not really, if the solution doesn't have multiple projects to build in parallel. For intra-project compilation parallelism, you need the above ``conf``.

```python
 njobs = conanfile.conf.get("tools.build:jobs",
                               default=_cpu_count(),
                               check_type=int)
```
This is how the default number of jobs is computed, by default it is already the ``cpu_count``, and that is used for the compilation parallelism

I see it seems there is a small bug in the ``max_cpu_count`` management for ``CMake``, but need clarification if you are using it or not.

One thing more: the ``max_cpu_count`` is not set by default to ``cpu_count``, because our experience is that having both ``/MP`` and ``/m:x`` flags at the same time to the number of cpus, is counter productive, it can easily become slower because having too many concurrent process. I wouldn't recommend setting ``max_cpu_count`` to the number of cpu-counts, at least without deactivating or reducing first the ``tools.build:jobs``



Patch:
diff --git a/conan/tools/cmake/cmake.py b/conan/tools/cmake/cmake.py
index 2efa8f37a9b..a94bae711c0 100644
--- a/conan/tools/cmake/cmake.py
+++ b/conan/tools/cmake/cmake.py
@@ -20,7 +20,7 @@ def _cmake_cmd_line_args(conanfile, generator):
 
     maxcpucount = conanfile.conf.get("tools.microsoft.msbuild:max_cpu_count", check_type=int)
     if maxcpucount and "Visual Studio" in generator:
-        args.append("/m:{}".format(njobs))
+        args.append("/m:{}".format(maxcpucount))
 
     # Arguments for verbosity
     if "Visual Studio" in generator:


Test patch:
diff --git a/test/unittests/tools/cmake/test_cmake_cmd_line_args.py b/test/unittests/tools/cmake/test_cmake_cmd_line_args.py
index b1c7dd860bb..7aa8a2f2096 100644
--- a/test/unittests/tools/cmake/test_cmake_cmd_line_args.py
+++ b/test/unittests/tools/cmake/test_cmake_cmd_line_args.py
@@ -12,6 +12,7 @@ def conanfile():
     c = ConfDefinition()
     c.loads(textwrap.dedent("""\
         tools.build:jobs=10
+        tools.microsoft.msbuild:max_cpu_count=23
     """))
 
     conanfile = ConanFileMock()
@@ -39,7 +40,7 @@ def test_ninja(conanfile):
 
 def test_visual_studio(conanfile):
     args = _cmake_cmd_line_args(conanfile, 'Visual Studio 16 2019')
-    assert [] == args
+    assert ["/m:23"] == args
 
     args = _cmake_cmd_line_args(conanfile, 'Ninja')
     assert args == ['-j10']