Skip to content

Commit

Permalink
Feature/meson subproject (#15916)
Browse files Browse the repository at this point in the history
* Add subproject to meson

* Reword variable and add unit test

* Don't restrict Linux to GCC 9

* Add integration test

* Revert change to _base.py

* Simplified test

* Requiring cmake tool

* Safe option conversion

* Renamed meson options file

* msvc fix

---------

Co-authored-by: Francisco Ramirez de Anton <franchuti688@gmail.com>
  • Loading branch information
fnadeau and franramirez688 authored Mar 25, 2024
1 parent 80f8b7d commit 87a7a1a
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ OK
To run specific tests, you can specify the test name too, something like:

```bash
$ python -m pytest conans/test/unittests/client/cmd/export_test.py::ExportTest::test_export_warning -s
$ python -m pytest conans/test/functional/command/export_test.py::TestRevisionModeSCM::test_revision_mode_scm -s
```

The `-s` argument can be useful to see some output that otherwise is captured by *pytest*.
Expand Down
19 changes: 19 additions & 0 deletions conan/tools/meson/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ class MesonToolchain(object):
{{it}} = {{value}}
{% endfor %}
{% for subproject, listkeypair in subproject_options -%}
[{{subproject}}:project options]
{% for keypair in listkeypair -%}
{% for it, value in keypair.items() -%}
{{it}} = {{value}}
{% endfor %}
{% endfor %}
{% endfor %}
[binaries]
{% if c %}c = {{c}}{% endif %}
{% if cpp %}cpp = {{cpp}}{% endif %}
Expand Down Expand Up @@ -146,6 +155,8 @@ def __init__(self, conanfile, backend=None):
# Add all the default dirs
self.project_options.update(self._get_default_dirs())

self.subproject_options = {}

#: Defines the Meson ``pkg_config_path`` variable
self.pkg_config_path = self._conanfile.generators_folder
#: Defines the Meson ``build.pkg_config_path`` variable (build context)
Expand Down Expand Up @@ -393,11 +404,19 @@ def _sanitize_format(v):
if self.gcc_cxx11_abi:
self.cpp_args.append("-D{}".format(self.gcc_cxx11_abi))

subproject_options = {}
for subproject, listkeypair in self.subproject_options.items():
if listkeypair is not None and listkeypair is not []:
subproject_options[subproject] = []
for keypair in listkeypair:
subproject_options[subproject].append({k: to_meson_value(v) for k, v in keypair.items()})

return {
# https://mesonbuild.com/Machine-files.html#properties
"properties": {k: to_meson_value(v) for k, v in self.properties.items()},
# https://mesonbuild.com/Machine-files.html#project-specific-options
"project_options": {k: to_meson_value(v) for k, v in self.project_options.items()},
"subproject_options": subproject_options.items(),
# https://mesonbuild.com/Builtin-options.html#directories
# https://mesonbuild.com/Machine-files.html#binaries
# https://mesonbuild.com/Reference-tables.html#compiler-and-linker-selection-variables
Expand Down
186 changes: 186 additions & 0 deletions conans/test/functional/toolchains/meson/test_subproject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import os
import textwrap

import pytest

from conans.test.assets.sources import gen_function_c
from conans.test.utils.tools import TestClient

_conanfile_py = textwrap.dedent("""
import os
import shutil
from conan import ConanFile
from conan.tools.meson import Meson, MesonToolchain
class App(ConanFile):
settings = "os", "arch", "compiler", "build_type"
options = {"shared": [True, False], "fPIC": [True, False], "use_french": ['true', 'false']}
default_options = {"shared": False, "fPIC": True, "use_french": 'false'}
exports_sources = "**"
def config_options(self):
if self.settings.os == "Windows":
self.options.rm_safe("fPIC")
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
def layout(self):
self.folders.build = "build"
def generate(self):
tc = MesonToolchain(self)
tc.subproject_options["hello"] = [{'french': str(self.options.use_french)}]
tc.generate()
def build(self):
meson = Meson(self)
meson.configure()
meson.build()
def package(self):
meson = Meson(self)
meson.install()
# https://mesonbuild.com/FAQ.html#why-does-building-my-project-with-msvc-output-static-libraries-called-libfooa
if self.settings.compiler == 'msvc' and not self.options.shared:
shutil.move(os.path.join(self.package_folder, "lib", "libhello.a"),
os.path.join(self.package_folder, "lib", "hello.lib"))
shutil.move(os.path.join(self.package_folder, "lib", "libgreeter.a"),
os.path.join(self.package_folder, "lib", "greeter.lib"))
def package_info(self):
self.cpp_info.components["hello"].libs = ['hello']
self.cpp_info.components["greeter"].libs = ['greeter']
self.cpp_info.components["greeter"].requires = ['hello']
""")

_meson_build = textwrap.dedent("""
project('greeter', 'c')
hello_proj = subproject('hello')
hello_dep = hello_proj.get_variable('hello_dep')
inc = include_directories('include')
greeter = static_library('greeter',
'greeter.c',
include_directories : inc,
dependencies : hello_dep,
install : true)
install_headers('greeter.h')
""")

_meson_subproject_build = textwrap.dedent("""
project('hello', 'c')
hello_c_args = []
if get_option('french')
hello_c_args = ['-DFRENCH']
endif
inc = include_directories('include')
hello = static_library('hello',
'hello.c',
include_directories : inc,
c_args: hello_c_args,
install : true)
hello_dep = declare_dependency(include_directories : inc, link_with : hello)
""")

_meson_subproject_options = textwrap.dedent("""
option('french', type : 'boolean', value : false)
""")

_hello_c = textwrap.dedent("""
#include <stdio.h>
void hello(void) {
#ifdef FRENCH
printf("Le sous-projet vous salut\\n");
#else
printf("Hello from subproject\\n");
#endif
}
""")

_greeter_c = textwrap.dedent("""
#include <hello.h>
void greeter(void) {
hello();
}
""")

_hello_h = textwrap.dedent("""
#pragma once
void hello(void);
""")

_greeter_h = textwrap.dedent("""
#pragma once
void greeter(void);
""")

_test_package_conanfile_py = textwrap.dedent("""
import os
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
from conan.tools.build import cross_building
class TestConan(ConanFile):
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeToolchain", "CMakeDeps"
def requirements(self):
self.requires(self.tested_reference_str)
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def test(self):
if not cross_building(self):
cmd = os.path.join(self.cpp.build.bindirs[0], "test_package")
self.run(cmd, env=["conanrunenv"])
""")

_test_package_cmake_lists = textwrap.dedent("""
cmake_minimum_required(VERSION 3.1)
project(test_package C)
add_executable(${PROJECT_NAME} src/test_package.c)
find_package(greeter CONFIG REQUIRED)
target_link_libraries(${PROJECT_NAME} greeter::greeter)
""")


@pytest.mark.tool("cmake")
@pytest.mark.tool("meson")
def test_subproject():
client = TestClient()
test_package_c = gen_function_c(name="main", includes=["greeter"], calls=["greeter"])
client.save({"conanfile.py": _conanfile_py,
"meson.build": _meson_build,
"greeter.c": _greeter_c,
"greeter.h": _greeter_h,
os.path.join("include", "greeter.h"): _greeter_h,
os.path.join("subprojects", "hello", "include", "hello.h"): _hello_h,
os.path.join("subprojects", "hello", "hello.c"): _hello_c,
os.path.join("subprojects", "hello", "meson.build"): _meson_subproject_build,
os.path.join("subprojects", "hello", "meson_options.txt"): _meson_subproject_options,
os.path.join("test_package", "conanfile.py"): _test_package_conanfile_py,
os.path.join("test_package", "CMakeLists.txt"): _test_package_cmake_lists,
os.path.join("test_package", "src", "test_package.c"): test_package_c})
client.run("create . --name=greeter --version=0.1")
assert "Hello from subproject" in client.out
assert "Le sous-projet vous salut" not in client.out
# Using subproject options
client.run("create . --name=greeter --version=0.1 -o use_french='true'")
assert "Hello from subproject" not in client.out
assert "Le sous-projet vous salut" in client.out
25 changes: 25 additions & 0 deletions conans/test/integration/toolchains/meson/test_mesontoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,28 @@ def build(self):
})
client.run("export tool")
client.run("create consumer -pr:h host -pr:b build --build=missing")


def test_subproject_options():
t = TestClient()
conanfile = textwrap.dedent("""
from conan import ConanFile
from conan.tools.meson import MesonToolchain
class Pkg(ConanFile):
settings = "os", "compiler", "arch", "build_type"
def generate(self):
tc = MesonToolchain(self)
tc.subproject_options["subproject1"] = [{"option1": "enabled"}, {"option2": "disabled"}]
tc.subproject_options["subproject2"] = [{"option3": "enabled"}]
tc.subproject_options["subproject2"].append({"option4": "disabled"})
tc.generate()
""")
t.save({"conanfile.py": conanfile})
t.run("install .")
content = t.load(MesonToolchain.native_filename)
assert "[subproject1:project options]" in content
assert "[subproject2:project options]" in content
assert "option1 = 'enabled'" in content
assert "option2 = 'disabled'" in content
assert "option3 = 'enabled'" in content
assert "option4 = 'disabled'" in content

0 comments on commit 87a7a1a

Please sign in to comment.