Repository: mesonbuild/meson

Issue:
host zlib is found using a cross file.
**Describe the bug**
When using a cross file which is not setting pkg_config_libdir, meson find the host version of zlib with

```
MESON build-cross --cross-file=path_to/my-cross-file.txt
```

**To Reproduce**

Meson file:
```
project('hellomeson', 'c')
cc = meson.get_compiler('c')
libz_dep = dependency('zlib', required : false)
if not libz_dep.found()
warning('unable to find libz')
endif
executable('hello-meson', 'src/hello.c')
```

Cross file:

```
[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'arm64'
endian = 'little'

[properties]
c_args = []
cpp_args = []
objc_args = []
objcpp_args = []
c_link_args = []
cpp_link_args = []
objc_link_args = []
objcpp_link_args = []
pkg_config_libdir = ['__unknown_sysroot__']


[binaries]
c = ['aarch64-linux-gnu-gcc']
cpp = ['aarch64-linux-gnu-g++']
ar = ['aarch64-linux-gnu-ar']
pkgconfig = 'pkg-config'
strip = ['aarch64-linux-gnu-strip']
```

**Expected behavior**
To not find any zlib or at least not the one from my host and then use it..

**system parameters**
* Is this a [cross build](https://mesonbuild.com/Cross-compilation.html) or just a plain native build (for the same computer)?
cross build
* what operating system (e.g. MacOS Catalina, Windows 10, CentOS 8.0, Ubuntu 18.04, etc.)
Ubuntu 18.04
* what Python version are you using e.g. 3.8.0
3.6.9
* what `meson --version`
master
* what `ninja --version` if it's a Ninja build
1.8.2



Discussion:
```
Found pkg-config: /usr/bin/pkg-config (0.29.1)
Determining dependency 'zlib' with pkg-config executable '/usr/bin/pkg-config'
PKG_CONFIG_PATH: 
PKG_CONFIG_LIBDIR: __unknown_sysroot__
Called `/usr/bin/pkg-config --modversion zlib` -> 1

CMake binary for MachineChoice.BUILD is not cached
None of 'CMAKE_FOR_BUILD' are defined in the environment, not changing global flags.
CMake binary missing from cross or native file, or env var undefined.
Trying a default CMake fallback at cmake
Found CMake: /usr/bin/cmake (3.10.2)
None of 'CMAKE_PREFIX_PATH_FOR_BUILD' are defined in the environment, not changing global flags.
Extracting basic cmake information
Try CMake generator: auto
Calling CMake (['/usr/bin/cmake']) in /home/scerveau/DEV/tmp/hello-meson/build-xlnx/meson-private/cmake_ZLIB with:
  - "--trace"
  - "--trace-expand"
  - "--no-warn-unused-cli"
  - "-DCMAKE_TOOLCHAIN_FILE=/home/scerveau/DEV/tmp/hello-meson/build-xlnx/meson-private/cmake_ZLIB/CMakeMesonToolchainFile.cmake"
  - "."
  -- Module search paths:    ['/', '/opt', '/usr', '/usr/local']
  -- CMake root:             /usr/share/cmake-3.10
  -- CMake architectures:    ['i386-linux-gnu', 'x86_64-linux-gnu']
  -- CMake lib search paths: ['lib', 'lib32', 'lib64', 'libx32', 'share', 'lib/i386-linux-gnu', 'lib/x86_64-linux-gnu']

Determining dependency 'ZLIB' with CMake executable '/usr/bin/cmake'
Try CMake generator: auto
Calling CMake (['/usr/bin/cmake']) in /home/scerveau/DEV/tmp/hello-meson/build-xlnx/meson-private/cmake_ZLIB with:
  - "-DNAME=ZLIB"
  - "-DARCHS=i386-linux-gnu;x86_64-linux-gnu"
  - "-DVERSION="
  - "-DCOMPS="
  - "--trace"
  - "--trace-expand"
  - "--no-warn-unused-cli"
  - "-DCMAKE_TOOLCHAIN_FILE=/home/scerveau/DEV/tmp/hello-meson/build-xlnx/meson-private/cmake_ZLIB/CMakeMesonToolchainFile.cmake"
  - "."
Guessed CMake target 'ZLIB::ZLIB'
CMake TARGET:
  -- name:      ZLIB::ZLIB
  -- type:      UNKNOWN
  -- imported:  True
  -- properties: {
      'INTERFACE_INCLUDE_DIRECTORIES': ['/usr/include']
      'IMPORTED_CONFIGURATIONS': ['RELEASE']
      'IMPORTED_LOCATION_RELEASE': ['/usr/lib/x86_64-linux-gnu/libz.so']
     }
  -- tline: CMake TRACE: /usr/share/cmake-3.10/Modules/FindZLIB.cmake:123 add_library(['ZLIB::ZLIB', 'UNKNOWN', 'IMPORTED'])
Include Dirs:         ['/usr/include']
Compiler Definitions: []
Compiler Options:     []
Libraries:            ['/usr/lib/x86_64-linux-gnu/libz.so']
Run-time dependency zlib found: YES 1.2.11
Build targets in project: 1
```
It finds it on the system using cmake. Meson should not use system cmake when cross compiling, just like we do with pkgconfig, unless we can set something similar to PKG_CONFIG_LIBDIR for cmake?

CC @mensinda 
In that case, the new CMake properties in the cross/native files should be used to correctly set up the CMake search paths. See:
- https://mesonbuild.com/Machine-files.html#properties
- https://mesonbuild.com/Machine-files.html#cmake-variables
- https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html
Specifically `CMAKE_FIND_ROOT_PATH_MODE_*`, `CMAKE_SYSROOT`, `CMAKE_FIND_ROOT_PATH`, and `CMAKE_MODULE_PATH` should help here.
@mensinda you mean I must set specific vars in my cross file related to cmake ?

Dont you think we should prevent the use of cmake if this value are not set properly ?
or you can set cmake to `false` in your binaries section to prevent it from being used.
> @mensinda you mean I must set specific vars in my cross file related to make ?

Essentially yes.

Setting these vars automagically would be a breaking change in behavior and not even guaranteed to work in all cases. We currently only set easy stuff like the C and C++ compiler from other cross-file entries automatically. Search paths are a lot more tricky...
> or you can set cmake to `false` in your binaries section to prevent it from being used.

Unfortunatly this is not working but it should not be the options neither as I might need CMake in a subproject.

But I still dont understand why we are using CMake to detect that zlib is on the system and get it wrong at the end with:

libz_dep = dependency('zlib', required : false)

CMake is not official way to detect a dependency AFAIK
> CMake is not official way to detect a dependency AFAIK

It is, because some dependencies only provide the `*-config.cmake` files. The dependency search procedure is always (unless explicitly overwritten with the `method` kwarg) to first check pkg-config and then fall back to CMake and pick the first result that is detected.

Also, zlib is not a hard-coded dependency in meson, so we don't really have a way to say, wait this dep is actually supposed to have a pkg-config file and there is a slight possibility that CMake messes stuff up.
cmake = ['false'] is not disabling cmake use to get the dep in 0.56.0


Could you please try `cmake = false`?
where do you put this ? because in binaries its failing with 

ERROR: Invalid type False for binary 'cmake' in cross file


I am not sure entirely sure how disabling binaries works (most likely @dcbaker knows more here), as for fixing your actual problem, putting this section into your cross-file should work (assuming meson >= 0.56.x)

```
[cmake]

CMAKE_BUILD_WITH_INSTALL_RPATH     = 'ON'
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM  = 'NEVER'
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY  = 'ONLY'
CMAKE_FIND_ROOT_PATH_MODE_INCLUDE  = 'ONLY'
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE  = 'ONLY'
```
I think the bug is in CMakeDependency:
```
self.cmakebin = CMakeExecutor(environment, CMakeDependency.class_cmake_version, MachineChoice.BUILD, silent=self.silent)
```

It hardcodes `MachineChoice.BUILD` machine instead of using `self.for_machine`. When cross compiling looking for `MachineChoice.HOST` should return not-found unless you explicitly set cmake in your `[binaries]`.

@mensinda git blame says it's a regression from 7e58f33376119b53e01616139ad9354ce9cfe003.
This is a pretty serious regression, must be fixed in 0.56.x IMHO.
I changed it to `MachineChoice.BUILD` because there is no reason to ever use the CMake binary for the host machine. This would be similar to trying to use a meson version for the host machine.
So, I would say that this isn't a regression, but rather working as intended. Unless I have missed something.
Archlinux at least provides a cmake wrapper script that sets all the env variables for finding dependencies. 
We really need something better, but if you provide an executable that doesn't return 0 then meson will declare not found. The Unix false command always returns 1
> Archlinux at least provides a cmake wrapper script that sets all the env variables for finding dependencies.

This should all be done in a CMake toolchain file though... The same way we want all our cross stuff to come from our machine files. CMake is (primarily) a build system and *not* a dependency management system like pkg-config (although we can use it like one)
I think we should not have to specify anything about cmake in the cross-compilation file, given we *should* not use cmake.

For now, there is a serious regression in meson 0.56.0: cross-compilation is broken.
And I don't understand how we are supposed to workaround it?
Setting cmake=false does not work. What should we do? mandate meson < 0.56 in our projects?
> I think we should not have to specify anything about cmake in the cross-compilation file, given we should not use cmake.

Sorry, but this is wrong. CMake *is* a valid dependency backend in meson (just like `pkg-config`) and also capable of finding cross dependencies. However, it is sadly not correctly setup out of the box currently, resulting in this error.

>  And I don't understand how we are supposed to workaround it?

> What should we do?

Using the setup in https://github.com/mesonbuild/meson/issues/8028#issuecomment-734360181 to set the CMake variables (wich should cause CMake to behave correctly) should work for now if disabling CMake in the cross file doesn't work for some reason.

Also, to clarify, I agree that this is a valid issue and the current behavior should be fixed, I only disagree with using a cross CMake binary. The solution I would prefer is to set these variables automatically for the cross-case for the next point release, which should also fix the issue.
@mensinda you don't have to like it, but it *used* to work. That makes this a regression. a setup that worked in 0.55.x will stop working in 0.56 with no deprecation warning. Usually we have a policy of at least providing a few versions of "this is deprecated, do this other thing instead."
@dcbaker As I said, I do agree that this is a bug/regression and should be fixed. I just disagree on *how* it should be fixed (cross CMake binary vs. fixing the CMake variables).

Assuming both fixes provide the same outcome, I would prefer fixing the CMake variables
The workaround you mention (the cmake section in cross file) does not have any effect.
I still see this in the log:
        Determining dependency 'ZLIB' with CMake executable '/usr/bin/cmake'
@tmonjalo Thank you for testing. Just to be on the save side: Did you use a fresh build directory (caching issues, etc)?

Also, the `Determining dependency 'ZLIB' with CMake executable '/usr/bin/cmake'` line in the log is to be expected, since the `[cmake]` should *fix* the check in CMake and not *disable* it. Is the dependency still misdetected?

If yes, could you please provide the meson log (or at least the relevant CMake part)? Just for reverence, if it turns out that just adding a few variables is not enough to fix the dependency lookup, then I am also in favor of reverting to the old behavior.
Unless I greatly, misread, you're seeing the desired outcome that @mensinda wants you to see. You should be determining it using /usr/bin/cmake

The question is something completely different: are you still seeing that the detected zlib is the host zlib?
Yes zlib is still misdetected. And yes I restart a fresh build.
Please see the logs below.

[meson-log.txt](https://github.com/mesonbuild/meson/files/5632673/meson-log.txt)
[CMakeMesonToolchainFile.cmake.txt](https://github.com/mesonbuild/meson/files/5632678/CMakeMesonToolchainFile.cmake.txt)
I would propose to make such behaviour configurable with environment variables like MESON_DEP_CMAKE=false

In the same idea (not related to this issue), I would propose a variable MESON_CROSS_PREFIX=aarch64-linux-gnu-
because changing a local configuration in a cross file, which is committed in the project tree, is not convenient.

The fact that such variable does not exist makes me wonder whether environment variables are avoided in meson?
Meson environment variable support is basically only there to provide some compatibility with existing build scripts. All new functionality that does not have existing env vars is always implemented with either project options or cross/native files. So yes, we do try to avoid env vars.

Since my fix didn't work, I will revert to the old behavior unless I figure out something else.
OK thanks.

Resolving patch:
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -850,6 +850,7 @@ def __init__(self, build: 'Build', subdir: Path, src_dir: Path, install_prefix:
 
     def configure(self, extra_cmake_options: T.List[str]) -> CMakeExecutor:
         # Find CMake
+        # TODO: Using MachineChoice.BUILD should always be correct here, but also evaluate the use of self.for_machine
         cmake_exe = CMakeExecutor(self.env, '>=3.7', MachineChoice.BUILD)
         if not cmake_exe.found():
             raise CMakeException('Unable to find CMake')
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -1098,10 +1098,11 @@ def __init__(self, name: str, environment: Environment, kwargs, language: T.Opti
         # AttributeError exceptions in derived classes
         self.traceparser = None  # type: CMakeTraceParser
 
-        self.cmakebin = CMakeExecutor(environment, CMakeDependency.class_cmake_version, MachineChoice.BUILD, silent=self.silent)
+        # TODO further evaluate always using MachineChoice.BUILD
+        self.cmakebin = CMakeExecutor(environment, CMakeDependency.class_cmake_version, self.for_machine, silent=self.silent)
         if not self.cmakebin.found():
             self.cmakebin = None
-            msg = 'No CMake binary for machine {} not found. Giving up.'.format(MachineChoice.BUILD)
+            msg = 'No CMake binary for machine {} not found. Giving up.'.format(self.for_machine)
             if self.required:
                 raise DependencyException(msg)
             mlog.debug(msg)


Test patch: