Repository: mesonbuild/meson

Issue:
`c_compiler.get_define()` returns a string containing a whitespace even though the define is not defined
**Describe the bug**
`cc.get_define()` is expected to return an empty string if the define is not defined. However, using specific MSVC version this doesn't seem to be guaranteed.
This issue is based on the findings in https://github.com/mesonbuild/wrapdb/pull/350

MSVC seems to print random spaces. I think instead of using the current code:
```c
        #ifndef DEFINE
        # define DEFINE
        #endif
        "MESON_GET_DEFINE_DELIMITER"
DEFINE
```
```c
        #ifndef DEFINE
        "MESON_GET_DEFINE_NDEFINE"
        #endif
        "MESON_GET_DEFINE_DELIMITER"
DEFINE
```
may be more robust? (This is just something I noticed while reading logs.)

**To Reproduce**
Build the libpng wrap using msvc via the x64 native tools command prompt for VS 2019.

**Expected behavior**
`c_compiler.get_define()` should return an empty string if the define is not defined.

**system parameters**
* Native Build
* Windows 10
* Py: 3.10.2
* 0.61.2


I have to leave my computer now but I wanted to post this issue beforehand. I am happy to provide more info and improve the ticket when I am back.
Fix `c_compiler`.get_define parsing unintentional whitespace as defined variable
This is my attempt to fix https://github.com/mesonbuild/meson/issues/10179
I don't use python often and I had trouble setting up my dev environment for meson.

I feel like using this more explicit path should be robuster against wrong whitespaces.


Discussion:
I'm running into the same problem (meson 0.61.1 and msvc 2022)
It would be nice to get a minimal reproducer to throw in `test cases` that way it doesn't regress.
I am on holiday for 1,5 weeks now. I know, I have seen some info about how to make tests but that is nothing I can do on the fly. Anyway, I *think* this should be enough to reproduce this behaviour:
```python
cc = meson.get_compiler('c')
assert(cc.get_define('THIS_IS_PROBABLY_NOT_DEFINED') == '')
assert(cc.get_define('__DATE__) != '')
```

Would that test fail on Meson master?
Well... I hope so. The first use case is basically the issue I encountered to begin with (see the linked issue and the linked pr in the linked issue). It *may* fail on some MSVC instances (it did reliably when meson was invoked using the msvc dev command prompt). Instead of redefining and relying on the delimiter to check if the define is defined, I just added another text that can be checked against. So the check itself is more robust. This is basically juggling with standard output.
I fixed the bugs in that snippet and neither of the asserts failed for me.
Do you invoked meson using the x64 native command prompt? I only reliably reproduced it there. One with GH Actions and using the dev command prompt. Alternately you can try to build the libpng wrap in a project (again if invoked using the dev tools).
I can't reproduce this issue neither, something is weird with @HaSa1002 setup, as far as I know he's the only one who ever had that issue. Not saying he did anything wrong, but I feel we haven't found the real root cause here. It could be his Windows language has an effect, or a bug in a very specific MSVC version, some extra cflag... who knows...
FWIW, I can't reproduce this bug with the latest release either; I'm guessing there's something else besides just VS 2019 needed to cause the issue.
Here's a log of what I did:
```
C:\Users\GregSkyles>cd "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\"

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community>%comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
**********************************************************************
** Visual Studio 2019 Developer Command Prompt v16.10.2
** Copyright (c) 2021 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community>cd \Users\GregSkyles\source\test-vs-env

C:\Users\GregSkyles\source\test-vs-env>meson init test-vs-env
Using "test-vs-env" (name of current directory) as project name.
Using "test-vs-env" (project name) as name of executable to build.
Defaulting to generating a C language project.
Sample project created. To build it run the
following commands:

meson setup builddir
meson compile -C builddir


C:\Users\GregSkyles\source\test-vs-env>rem  edit meson.build to add asserts

C:\Users\GregSkyles\source\test-vs-env>type meson.build
project('test-vs-env', 'c',
  version : '0.1',
  default_options : ['warning_level=3'])

exe = executable('test_vs_env', 'test_vs_env.c',
  install : true)

cc = meson.get_compiler('c')
assert(cc.get_define('THIS_IS_PROBABLY_NOT_DEFINED') == '')
assert(cc.get_define('__DATE__') != '')

test('basic', exe)

C:\Users\GregSkyles\source\test-vs-env>meson setup build .
The Meson build system
Version: 0.62.0
Source dir: C:\Users\GregSkyles\source\test-vs-env
Build dir: C:\Users\GregSkyles\source\test-vs-env\build
Build type: native build
Project name: test-vs-env
Project version: 0.1
C compiler for the host machine: cl (msvc 19.29.30038.1 "Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30038.1 for x64")
C linker for the host machine: link link 14.29.30038.1
Host machine cpu family: x86_64
Host machine cpu: x86_64
Fetching value of define "THIS_IS_PROBABLY_NOT_DEFINED" :
Fetching value of define "__DATE__" : "Mar 28 2022"
Build targets in project: 1

Found ninja-1.10.2 at "C:\Program Files\Meson\ninja.EXE"

C:\Users\GregSkyles\source\test-vs-env>
```
That said, I think this PR is a good idea because it distinguish between undefined and defined with empty value which could be generally useful. At least I prefer that over the original idea of stdout.strip().
We have `get_define()` which can give us the value of a define "or empty", and we have `has_header_symbol()` which can check if a define exists "in a specific header". We don't have a `has_define()` that tells us the header symbol is specifically not a function, but a define, or that a define exists which is provided by the compiler and not a specific header.

Maybe we should have a has_define() so that future versions of Meson can distinguish between "does not exist" and "defined to empty", without get_define() knowing the difference but reporting both as `''`.
CI fails with:

```
test cases\common\132 get define\meson.build:15:4: ERROR: Assert failed: _WIN32 value is 1
        
         instead of 1
```

That's weird... There is definitely something going on with whitespaces. I think it's because the value is inside the `#else` case now. Wondering if we should have a delimiter before **and after** the value to be sure?
To be fair, nothing forbid the preprocessor to add whitespaces there if it wants, we are probably relying on UB here anyway.
# [Codecov](https://codecov.io/gh/mesonbuild/meson/pull/10182?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#10182](https://codecov.io/gh/mesonbuild/meson/pull/10182?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (c69fa83) into [master](https://codecov.io/gh/mesonbuild/meson/commit/b0d300e788571f48e95d6e673c6f94e54d1dd355?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (b0d300e) will **decrease** coverage by `4.41%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master   #10182      +/-   ##
==========================================
- Coverage   66.15%   61.73%   -4.42%     
==========================================
  Files         406      203     -203     
  Lines       86576    43446   -43130     
  Branches    19117     9655    -9462     
==========================================
- Hits        57271    26823   -30448     
+ Misses      24895    14529   -10366     
+ Partials     4410     2094    -2316     
```


| [Impacted Files](https://codecov.io/gh/mesonbuild/meson/pull/10182?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [mesonbuild/compilers/mixins/clike.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9jb21waWxlcnMvbWl4aW5zL2NsaWtlLnB5) | `77.24% <100.00%> (-5.32%)` | :arrow_down: |
| [mesonbuild/mesonlib/win32.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9tZXNvbmxpYi93aW4zMi5weQ==) | `0.00% <0.00%> (-100.00%)` | :arrow_down: |
| [mesonbuild/scripts/clangtidy.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9zY3JpcHRzL2NsYW5ndGlkeS5weQ==) | `0.00% <0.00%> (-93.34%)` | :arrow_down: |
| [mesonbuild/scripts/depscan.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9zY3JpcHRzL2RlcHNjYW4ucHk=) | `0.00% <0.00%> (-83.81%)` | :arrow_down: |
| [mesonbuild/scripts/hotdochelper.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9zY3JpcHRzL2hvdGRvY2hlbHBlci5weQ==) | `0.00% <0.00%> (-81.49%)` | :arrow_down: |
| [mesonbuild/modules/unstable\_wayland.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9tb2R1bGVzL3Vuc3RhYmxlX3dheWxhbmQucHk=) | `0.00% <0.00%> (-79.32%)` | :arrow_down: |
| [mesonbuild/modules/unstable\_cuda.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9tb2R1bGVzL3Vuc3RhYmxlX2N1ZGEucHk=) | `0.00% <0.00%> (-72.50%)` | :arrow_down: |
| [mesonbuild/modules/windows.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9tb2R1bGVzL3dpbmRvd3MucHk=) | `0.00% <0.00%> (-71.72%)` | :arrow_down: |
| [mesonbuild/modules/java.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9tb2R1bGVzL2phdmEucHk=) | `0.00% <0.00%> (-71.43%)` | :arrow_down: |
| [mesonbuild/scripts/gtkdochelper.py](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bWVzb25idWlsZC9zY3JpcHRzL2d0a2RvY2hlbHBlci5weQ==) | `0.00% <0.00%> (-69.60%)` | :arrow_down: |
| ... and [289 more](https://codecov.io/gh/mesonbuild/meson/pull/10182/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/mesonbuild/meson/pull/10182?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/mesonbuild/meson/pull/10182?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Last update [0022e18...c69fa83](https://codecov.io/gh/mesonbuild/meson/pull/10182?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None).


Resolving patch:
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -688,7 +688,7 @@ def get_define(self, dname: str, prefix: str, env: 'Environment',
         # Get the preprocessed value after the delimiter,
         # minus the extra newline at the end and
         # merge string literals.
-        return self._concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]), cached
+        return self._concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]).strip(), cached
 
     def get_return_value(self, fname: str, rtype: str, prefix: str,
                          env: 'Environment', extra_args: T.Optional[T.List[str]],


Test patch:
diff --git a/test cases/common/132 get define/test.json b/test cases/common/132 get define/test.json
new file mode 100644
--- /dev/null
+++ b/test cases/common/132 get define/test.json	
@@ -0,0 +1,10 @@
+{
+  "matrix": {
+    "options": {
+      "c_std": [
+        { "val": "none" },
+        { "val": "c11" }
+      ]
+    }
+  }
+}