From fc3317911b43a8a802881fd4fdc28325a9cdee22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 10 Jan 2023 09:22:47 +0100 Subject: [PATCH 1/7] Make new cmake_{lib,exe} able to require things --- conan/api/subapi/new.py | 21 ++++++--- conan/cli/commands/new.py | 3 +- conan/internal/api/new/cmake_exe.py | 23 +++++++++- conan/internal/api/new/cmake_lib.py | 50 +++++++++++++++++++--- conans/test/functional/command/new_test.py | 26 +++++++++++ 5 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 conans/test/functional/command/new_test.py diff --git a/conan/api/subapi/new.py b/conan/api/subapi/new.py index e102798b756..4080e054f4d 100644 --- a/conan/api/subapi/new.py +++ b/conan/api/subapi/new.py @@ -9,7 +9,6 @@ class NewAPI: - _NOT_TEMPLATES = "not_templates" # Filename containing filenames of files not to be rendered def __init__(self, conan_api): @@ -84,17 +83,29 @@ def _read_files(self, template_folder): return template_files, non_template_files - @staticmethod def render(template_files, definitions): result = {} name = definitions.get("name", "Pkg") definitions["conan_version"] = __version__ - definitions["package_name"] = name.replace("-", "_").replace("+", "_").replace(".", "_") + + def as_package_name(n): + return n.replace("-", "_").replace("+", "_").replace(".", "_") + + def as_name(ref): + ref = as_package_name(ref) + if '/' in ref: + ref = ref[0:ref.index('/')] + return ref + + definitions["package_name"] = as_package_name(name) definitions["as_iterable"] = lambda x: [x] if isinstance(x, str) else x + definitions["as_name"] = as_name for k, v in template_files.items(): - k = Template(k, keep_trailing_newline=True, undefined=StrictUndefined).render(**definitions) - v = Template(v, keep_trailing_newline=True, undefined=StrictUndefined).render(**definitions) + k = Template(k, keep_trailing_newline=True, undefined=StrictUndefined).render( + **definitions) + v = Template(v, keep_trailing_newline=True, undefined=StrictUndefined).render( + **definitions) if v: result[k] = v return result diff --git a/conan/cli/commands/new.py b/conan/cli/commands/new.py index e8c2011b975..05a58233eaf 100644 --- a/conan/cli/commands/new.py +++ b/conan/cli/commands/new.py @@ -68,7 +68,8 @@ def get_template_vars(): template_vars.extend(meta.find_undeclared_variables(ast)) injected_vars = {"conan_version", "package_name"} - template_vars = list(set(template_vars) - injected_vars) + optional_vars = {"requires"} + template_vars = list(set(template_vars) - injected_vars - optional_vars) template_vars.sort() return template_vars diff --git a/conan/internal/api/new/cmake_exe.py b/conan/internal/api/new/cmake_exe.py index aab9b0af83b..a5a57aa0e25 100644 --- a/conan/internal/api/new/cmake_exe.py +++ b/conan/internal/api/new/cmake_exe.py @@ -1,7 +1,7 @@ from conan.internal.api.new.cmake_lib import source_cpp, source_h, test_main conanfile_exe = '''from conan import ConanFile -from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps class {{package_name}}Recipe(ConanFile): @@ -26,6 +26,8 @@ def layout(self): cmake_layout(self) def generate(self): + deps = CMakeDeps(self) + deps.generate() tc = CMakeToolchain(self) tc.generate() @@ -37,13 +39,32 @@ def build(self): def package(self): cmake = CMake(self) cmake.install() + + {% if requires is defined -%} + def requirements(self): + {% for require in as_iterable(requires) -%} + self.requires("{{ require }}") + {% endfor %} + {%- endif %} ''' cmake_exe_v2 = """cmake_minimum_required(VERSION 3.15) project({{name}} CXX) +{% if requires is defined -%} +{% for require in as_iterable(requires) -%} +find_package({{as_name(require)}} CONFIG REQUIRED) +{% endfor %} +{%- endif %} + add_executable({{name}} src/{{name}}.cpp src/main.cpp) +{% if requires is defined -%} +{% for require in as_iterable(requires) -%} +target_link_libraries({{name}} PRIVATE {{as_name(require)}}::{{as_name(require)}}) +{% endfor %} +{%- endif %} + install(TARGETS {{name}} DESTINATION "." RUNTIME DESTINATION bin ARCHIVE DESTINATION lib diff --git a/conan/internal/api/new/cmake_lib.py b/conan/internal/api/new/cmake_lib.py index e1c404df0a3..187172bad08 100644 --- a/conan/internal/api/new/cmake_lib.py +++ b/conan/internal/api/new/cmake_lib.py @@ -1,5 +1,5 @@ conanfile_sources_v2 = '''from conan import ConanFile -from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps class {{package_name}}Recipe(ConanFile): @@ -29,6 +29,8 @@ def layout(self): cmake_layout(self) def generate(self): + deps = CMakeDeps(self) + deps.generate() tc = CMakeToolchain(self) tc.generate() @@ -43,34 +45,66 @@ def package(self): def package_info(self): self.cpp_info.libs = ["{{name}}"] + + {% if requires is defined -%} + def requirements(self): + {% for require in requires -%} + self.requires("{{ require }}") + {% endfor %} + {%- endif %} ''' cmake_v2 = """cmake_minimum_required(VERSION 3.15) project({{name}} CXX) +{% if requires is defined -%} +{% for require in as_iterable(requires) -%} +find_package({{as_name(require)}} CONFIG REQUIRED) +{% endfor %} +{%- endif %} + + add_library({{name}} src/{{name}}.cpp) target_include_directories({{name}} PUBLIC include) +{% if requires is defined -%} +{% for require in as_iterable(requires) -%} +target_link_libraries({{name}} PRIVATE {{as_name(require)}}::{{as_name(require)}}) +{% endfor %} +{%- endif %} + set_target_properties({{name}} PROPERTIES PUBLIC_HEADER "include/{{name}}.h") install(TARGETS {{name}}) """ source_h = """#pragma once -{% set define_name = name.replace("-", "_").replace("+", "_").replace(".", "_").upper() %} +{% set define_name = package_name.upper() %} #ifdef _WIN32 #define {{define_name}}_EXPORT __declspec(dllexport) #else #define {{define_name}}_EXPORT #endif -{{define_name}}_EXPORT void {{name.replace("-", "_").replace("+", "_").replace(".", "_")}}(); +{{define_name}}_EXPORT void {{package_name}}(); """ source_cpp = r"""#include #include "{{name}}.h" +{% if requires is defined -%} +{% for require in as_iterable(requires) -%} +#include "{{ as_name(require) }}.h" +{% endfor %} +{%- endif %} + + +void {{package_name}}(){ + {% if requires is defined -%} + {% for require in as_iterable(requires) -%} + {{ as_name(require) }}(); + {% endfor %} + {%- endif %} -void {{name.replace("-", "_").replace("+", "_").replace(".", "_")}}(){ #ifdef NDEBUG std::cout << "{{name}}/{{version}}: Hello World Release!\n"; #else @@ -198,6 +232,12 @@ def test(self): find_package({{name}} CONFIG REQUIRED) +{% if requires is defined -%} +{% for require in as_iterable(requires) -%} +find_package({{as_name(require)}} CONFIG REQUIRED) +{% endfor %} +{%- endif %} + add_executable(example src/example.cpp) target_link_libraries(example {{name}}::{{name}}) """ @@ -206,7 +246,7 @@ def test(self): test_main = """#include "{{name}}.h" int main() { - {{name.replace("-", "_").replace("+", "_").replace(".", "_")}}(); + {{package_name}}(); } """ diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py new file mode 100644 index 00000000000..7671c8517fb --- /dev/null +++ b/conans/test/functional/command/new_test.py @@ -0,0 +1,26 @@ +import pytest as pytest + +from utils.tools import TestClient + + +@pytest.mark.tool("cmake") +def test_diamonds_cmake_with_new(): + tc = TestClient() + tc.run("new cmake_lib -d name=math -d version=3.14") + tc.run("create .") + tc.run("new cmake_lib -d name=ai -d version=0.1 -d requires=ai/3.14 -f") + tc.run("create .") + assert "math/1.3: Hello World" in tc.out + tc.run("new cmake_lib -d name=colours -d version=42.0 -f") + tc.run("create .") + tc.run("new cmake_lib -d name=ui -d version=1.0 -d requires=math/3.14 -d requires=colours/42.0 -f") + tc.run("create .") + assert "math/1.3: Hello World" in tc.out + assert "colours/42.0: Hello World" in tc.out + tc.run("new cmake_exe -d name=game -d version=0.0 -d requires=math/3.14 -d requires=ai/0.1 -d requires=ui/1.0 -f") + tc.run("create .") + assert "math/1.3: Hello World" in tc.out + assert "ai/0.1: Hello World" in tc.out + assert "ui/1.0: Hello World" in tc.out + + From dee302cc3f038335dc48083269a4cce8ab07df02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 10 Jan 2023 11:38:50 +0100 Subject: [PATCH 2/7] Add forggoten changes --- conan/cli/commands/new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conan/cli/commands/new.py b/conan/cli/commands/new.py index 05a58233eaf..8bfe01e6fd8 100644 --- a/conan/cli/commands/new.py +++ b/conan/cli/commands/new.py @@ -67,7 +67,7 @@ def get_template_vars(): ast = env.parse(templ_str) template_vars.extend(meta.find_undeclared_variables(ast)) - injected_vars = {"conan_version", "package_name"} + injected_vars = {"conan_version", "package_name", "as_iterable", "as_name"} optional_vars = {"requires"} template_vars = list(set(template_vars) - injected_vars - optional_vars) template_vars.sort() From e7e31258c3339d53612d4300a56add5369bb76ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 10 Jan 2023 12:54:57 +0100 Subject: [PATCH 3/7] Fix bug and simplify test --- conan/internal/api/new/cmake_lib.py | 2 +- conans/test/functional/command/new_test.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/conan/internal/api/new/cmake_lib.py b/conan/internal/api/new/cmake_lib.py index 187172bad08..f9e6bb2769e 100644 --- a/conan/internal/api/new/cmake_lib.py +++ b/conan/internal/api/new/cmake_lib.py @@ -48,7 +48,7 @@ def package_info(self): {% if requires is defined -%} def requirements(self): - {% for require in requires -%} + {% for require in as_iterable(requires) -%} self.requires("{{ require }}") {% endfor %} {%- endif %} diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py index 7671c8517fb..e11cb9c40d4 100644 --- a/conans/test/functional/command/new_test.py +++ b/conans/test/functional/command/new_test.py @@ -3,23 +3,20 @@ from utils.tools import TestClient -@pytest.mark.tool("cmake") +#@pytest.mark.tool("cmake") def test_diamonds_cmake_with_new(): tc = TestClient() tc.run("new cmake_lib -d name=math -d version=3.14") tc.run("create .") - tc.run("new cmake_lib -d name=ai -d version=0.1 -d requires=ai/3.14 -f") + tc.run("new cmake_lib -d name=ai -d version=0.1 -d requires=math/3.14 -f") tc.run("create .") - assert "math/1.3: Hello World" in tc.out - tc.run("new cmake_lib -d name=colours -d version=42.0 -f") + assert "math/3.14: Hello World" in tc.out + tc.run("new cmake_lib -d name=ui -d version=1.0 -d requires=math/3.14 -f") tc.run("create .") - tc.run("new cmake_lib -d name=ui -d version=1.0 -d requires=math/3.14 -d requires=colours/42.0 -f") + assert "math/3.14: Hello World" in tc.out + tc.run("new cmake_exe -d name=game -d version=0.0 -d requires=ai/0.1 -d requires=ui/1.0 -f") tc.run("create .") - assert "math/1.3: Hello World" in tc.out - assert "colours/42.0: Hello World" in tc.out - tc.run("new cmake_exe -d name=game -d version=0.0 -d requires=math/3.14 -d requires=ai/0.1 -d requires=ui/1.0 -f") - tc.run("create .") - assert "math/1.3: Hello World" in tc.out + assert "math/3.14: Hello World" in tc.out assert "ai/0.1: Hello World" in tc.out assert "ui/1.0: Hello World" in tc.out From 6ca95150b2b2c7010ac1c66dc03416a7c2029dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 10 Jan 2023 12:55:34 +0100 Subject: [PATCH 4/7] Mark test as needing CMake --- conans/test/functional/command/new_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py index e11cb9c40d4..5f2c72f91fc 100644 --- a/conans/test/functional/command/new_test.py +++ b/conans/test/functional/command/new_test.py @@ -3,7 +3,7 @@ from utils.tools import TestClient -#@pytest.mark.tool("cmake") +@pytest.mark.tool("cmake") def test_diamonds_cmake_with_new(): tc = TestClient() tc.run("new cmake_lib -d name=math -d version=3.14") From d39917c5592b7ee83e10e04cfd4b3cb400f1ba77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 10 Jan 2023 18:18:26 +0100 Subject: [PATCH 5/7] Fix import path, even though it works locally for me --- conans/test/functional/command/new_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py index 5f2c72f91fc..c3ec6e149ae 100644 --- a/conans/test/functional/command/new_test.py +++ b/conans/test/functional/command/new_test.py @@ -1,6 +1,6 @@ import pytest as pytest -from utils.tools import TestClient +from conans.test.utils.tools import TestClient @pytest.mark.tool("cmake") From ec0304ab89da080ffed47d0b82c3a2a4d1e49624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Tue, 10 Jan 2023 18:27:31 +0100 Subject: [PATCH 6/7] Add TODO to remind us to remove the new test once the feature is used elsewhere --- conans/test/functional/command/new_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py index c3ec6e149ae..3459a1fffaf 100644 --- a/conans/test/functional/command/new_test.py +++ b/conans/test/functional/command/new_test.py @@ -3,6 +3,7 @@ from conans.test.utils.tools import TestClient +# TODO: Remove this test once this feature is used elsewhere to test other things @pytest.mark.tool("cmake") def test_diamonds_cmake_with_new(): tc = TestClient() From 2389de71178cef728493ba473c074bf39e47004f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Rinc=C3=B3n=20Blanco?= Date: Wed, 11 Jan 2023 11:32:32 +0100 Subject: [PATCH 7/7] Math->Mathematics to fix Windows --- conans/test/functional/command/new_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/conans/test/functional/command/new_test.py b/conans/test/functional/command/new_test.py index 3459a1fffaf..a1a10952e1d 100644 --- a/conans/test/functional/command/new_test.py +++ b/conans/test/functional/command/new_test.py @@ -7,17 +7,17 @@ @pytest.mark.tool("cmake") def test_diamonds_cmake_with_new(): tc = TestClient() - tc.run("new cmake_lib -d name=math -d version=3.14") + tc.run("new cmake_lib -d name=mathematics -d version=3.14") tc.run("create .") - tc.run("new cmake_lib -d name=ai -d version=0.1 -d requires=math/3.14 -f") + tc.run("new cmake_lib -d name=ai -d version=0.1 -d requires=mathematics/3.14 -f") tc.run("create .") - assert "math/3.14: Hello World" in tc.out - tc.run("new cmake_lib -d name=ui -d version=1.0 -d requires=math/3.14 -f") + assert "mathematics/3.14: Hello World" in tc.out + tc.run("new cmake_lib -d name=ui -d version=1.0 -d requires=mathematics/3.14 -f") tc.run("create .") - assert "math/3.14: Hello World" in tc.out + assert "mathematics/3.14: Hello World" in tc.out tc.run("new cmake_exe -d name=game -d version=0.0 -d requires=ai/0.1 -d requires=ui/1.0 -f") tc.run("create .") - assert "math/3.14: Hello World" in tc.out + assert "mathematics/3.14: Hello World" in tc.out assert "ai/0.1: Hello World" in tc.out assert "ui/1.0: Hello World" in tc.out