Repository: conan-io/conan

Issue:
[bug] NMakeDeps: doesn't properly handle cpp_info.defines with special characters
### Environment details

* Operating System+version: Windows 11
* Compiler+version: Visual Studio 2022
* Conan version: 2.0.3
* Python version: 3.10.6


### Steps to reproduce

Call `conan install --requires="gdal/3.4.3" -o "sqlite3/*:shared=True" -b missing` (you may have to write a conanfile.py in order to override versions of some dependencies since there are some conflicts currently I think).

My analysis of this issue:
When compiler is Visual Studio, gdal recipe (if version < 3.5) relies on `NMakeToolchain` & `NMakeDeps`. sqlite3 is an optional dependency of gdal, but enabled by default. When sqlite3 is shared on Windows, its recipe appends `SQLITE_API=__declspec(dllimport)` into `cpp_info.defines` (https://github.com/conan-io/conan-center-index/blob/eb52bc7ae38ab3cd857d38b0d32be39c45500fa6/recipes/sqlite3/all/conanfile.py#L207-L209).

I guess that my PR https://github.com/conan-io/conan/pull/12944 is not robust to some special characters in these definitions, they may have to be protected more than what is currently implemented here: https://github.com/conan-io/conan/blob/2.0.3/conan/tools/microsoft/nmakedeps.py#L54

### Logs

I'll attach the log soon, but basically all compilation units of gdal including sqlite3.h fail. There is one error per line where `SQLITE_API` appears, something like 'string' unknown.


Discussion:
Some characters may have to be protected with `\`, lile `(` and `)`

Resolving patch:
diff --git a/conan/tools/microsoft/nmakedeps.py b/conan/tools/microsoft/nmakedeps.py
--- a/conan/tools/microsoft/nmakedeps.py
+++ b/conan/tools/microsoft/nmakedeps.py
@@ -51,9 +51,9 @@ def format_define(define):
                     # https://learn.microsoft.com/en-us/cpp/build/reference/cl-environment-variables
                     macro, value = define.split("=", 1)
                     if value and not value.isnumeric():
-                        value = f'\\"{value}\\"'
+                        value = f'\"{value}\"'
                     define = f"{macro}#{value}"
-                return f"/D{define}"
+                return f"/D\"{define}\""
 
             cl_flags = [f'-I"{p}"' for p in cpp_info.includedirs or []]
             cl_flags.extend(cpp_info.cflags or [])
diff --git a/conan/tools/microsoft/nmaketoolchain.py b/conan/tools/microsoft/nmaketoolchain.py
--- a/conan/tools/microsoft/nmaketoolchain.py
+++ b/conan/tools/microsoft/nmaketoolchain.py
@@ -38,7 +38,7 @@ def _format_defines(self, defines):
                 if value and not value.isnumeric():
                     value = f'\\"{value}\\"'
                 define = f"{macro}#{value}"
-            formated_defines.append(f"/D{define}")
+            formated_defines.append(f"/D\"{define}\"")
         return formated_defines
 
     @property


Test patch:
diff --git a/conans/test/functional/toolchains/test_nmake_toolchain.py b/conans/test/functional/toolchains/test_nmake_toolchain.py
--- a/conans/test/functional/toolchains/test_nmake_toolchain.py
+++ b/conans/test/functional/toolchains/test_nmake_toolchain.py
@@ -13,7 +13,8 @@
     [
         ("msvc", "190", "dynamic", "14", "Release", [], [], [], [], []),
         ("msvc", "190", "dynamic", "14", "Release",
-         ["TEST_DEFINITION1", "TEST_DEFINITION2=0", "TEST_DEFINITION3=", "TEST_DEFINITION4=TestPpdValue4"],
+         ["TEST_DEFINITION1", "TEST_DEFINITION2=0", "TEST_DEFINITION3=", "TEST_DEFINITION4=TestPpdValue4",
+          "TEST_DEFINITION5=__declspec(dllexport)", "TEST_DEFINITION6=foo bar"],
          ["/GL"], ["/GL"], ["/LTCG"], ["/LTCG"]),
         ("msvc", "191", "static", "17", "Debug", [], [], [], [], []),
     ],
diff --git a/conans/test/integration/toolchains/microsoft/test_nmakedeps.py b/conans/test/integration/toolchains/microsoft/test_nmakedeps.py
--- a/conans/test/integration/toolchains/microsoft/test_nmakedeps.py
+++ b/conans/test/integration/toolchains/microsoft/test_nmakedeps.py
@@ -28,7 +28,10 @@ def package_info(self):
                 self.cpp_info.components["pkg-3"].defines = ["TEST_DEFINITION3="]
                 self.cpp_info.components["pkg-3"].requires = ["pkg-1", "pkg-2"]
                 self.cpp_info.components["pkg-4"].libs = ["pkg-4"]
-                self.cpp_info.components["pkg-4"].defines = ["TEST_DEFINITION4=foo"]
+                self.cpp_info.components["pkg-4"].defines = ["TEST_DEFINITION4=foo",
+                                                            "TEST_DEFINITION5=__declspec(dllexport)",
+                                                            "TEST_DEFINITION6=foo bar",
+                                                            "TEST_DEFINITION7=7"]
     """)
     client.save({"conanfile.py": conanfile})
     client.run("create . -s arch=x86_64")
@@ -38,8 +41,11 @@ def package_info(self):
     bat_file = client.load("conannmakedeps.bat")
     # Checking that defines are added to CL
     for flag in (
-        r"/DTEST_DEFINITION1", r"/DTEST_DEFINITION2#0",
-        r"/DTEST_DEFINITION3#", r'/DTEST_DEFINITION4#\\"foo\\"',
+        '/D"TEST_DEFINITION1"', '/D"TEST_DEFINITION2#0"',
+        '/D"TEST_DEFINITION3#"', '/D"TEST_DEFINITION4#"foo""',
+        '/D"TEST_DEFINITION5#"__declspec\(dllexport\)""',
+        '/D"TEST_DEFINITION6#"foo bar""',
+        '/D"TEST_DEFINITION7#7"'
     ):
         assert re.search(fr'set "CL=%CL%.*\s{flag}(?:\s|")', bat_file)
     # Checking that libs and system libs are added to _LINK_