From c53dfe171ac2dec11007162592540526c1c452e3 Mon Sep 17 00:00:00 2001 From: memsharded Date: Thu, 30 Jun 2022 14:51:55 +0200 Subject: [PATCH 1/6] test layout sharing code --- .../functional/layout/test_in_subfolder.py | 52 +++++++++++++++++++ conans/test/integration/layout/__init__.py | 0 2 files changed, 52 insertions(+) create mode 100644 conans/test/integration/layout/__init__.py diff --git a/conans/test/functional/layout/test_in_subfolder.py b/conans/test/functional/layout/test_in_subfolder.py index 33925fa67b6..93bb03f6dcc 100644 --- a/conans/test/functional/layout/test_in_subfolder.py +++ b/conans/test/functional/layout/test_in_subfolder.py @@ -54,3 +54,55 @@ def build(self): c.run("build conan") assert "conanfile.py (pkg/0.1): MYCMAKE-BUILD: mycmake!" in c.out assert c.load("build/mylib.a") == "mylib" + + +def test_exports_sources_common_code(): + """ very similar to the above, but intended for a multi-package project sharing some + common code + """ + c = TestClient() + conanfile = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.files import load, copy, save + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + + def layout(self): + self.folders.root = ".." + self.folders.source = "pkg" + self.folders.build = "build" + + def export_sources(self): + source_folder = os.path.join(self.recipe_folder, "..") + copy(self, "*.txt", source_folder, self.export_sources_folder) + copy(self, "*.cmake", source_folder, self.export_sources_folder) + + def source(self): + cmake = load(self, "CMakeLists.txt") + self.output.info("MYCMAKE-SRC: {}".format(cmake)) + + def build(self): + path = os.path.join(self.source_folder, "CMakeLists.txt") + cmake = load(self, path) + self.output.info("MYCMAKE-BUILD: {}".format(cmake)) + + path = os.path.join(self.export_sources_folder, "common", "myutils.cmake") + cmake = load(self, path) + self.output.info("MYUTILS-BUILD: {}".format(cmake)) + """) + c.save({"pkg/conanfile.py": conanfile, + "pkg/CMakeLists.txt": "mycmake!", + "common/myutils.cmake": "myutils!"}) + c.run("create pkg") + assert "pkg/0.1: MYCMAKE-SRC: mycmake!" in c.out + assert "pkg/0.1: MYCMAKE-BUILD: mycmake!" in c.out + assert "pkg/0.1: MYUTILS-BUILD: myutils!" in c.out + + # Local flow + c.run("install pkg") + # SOURCE NOT CALLED! It doesnt make sense (will fail due to local exports) + c.run("build pkg") + assert "conanfile.py (pkg/0.1): MYCMAKE-BUILD: mycmake!" in c.out + assert "conanfile.py (pkg/0.1): MYUTILS-BUILD: myutils!" in c.out diff --git a/conans/test/integration/layout/__init__.py b/conans/test/integration/layout/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From c9a3a1ae0aa7e5000b561402209f1f5846863e44 Mon Sep 17 00:00:00 2001 From: memsharded Date: Fri, 8 Jul 2022 00:46:15 +0200 Subject: [PATCH 2/6] added cmake_layout subfolder --- conan/tools/cmake/layout.py | 6 +- .../functional/layout/test_in_subfolder.py | 61 +++++++++++++++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/conan/tools/cmake/layout.py b/conan/tools/cmake/layout.py index 18f78c5e3b6..a36429dca20 100644 --- a/conan/tools/cmake/layout.py +++ b/conan/tools/cmake/layout.py @@ -3,7 +3,7 @@ from conans.errors import ConanException -def cmake_layout(conanfile, generator=None, src_folder="."): +def cmake_layout(conanfile, generator=None, src_folder=".", subfolder=None): gen = conanfile.conf.get("tools.cmake.cmaketoolchain:generator", default=generator) if gen: multi = "Visual" in gen or "Xcode" in gen or "Multi-Config" in gen @@ -14,13 +14,13 @@ def cmake_layout(conanfile, generator=None, src_folder="."): else: multi = False - conanfile.folders.source = src_folder + conanfile.folders.source = src_folder if not subfolder else os.path.join(subfolder, src_folder) try: build_type = str(conanfile.settings.build_type) except ConanException: raise ConanException("'build_type' setting not defined, it is necessary for cmake_layout()") - build_folder = "build" + build_folder = "build" if not subfolder else os.path.join(subfolder, "build") custom_conf = get_build_folder_custom_vars(conanfile) if custom_conf: build_folder = "{}/{}".format(build_folder, custom_conf) diff --git a/conans/test/functional/layout/test_in_subfolder.py b/conans/test/functional/layout/test_in_subfolder.py index 93bb03f6dcc..c984c953f35 100644 --- a/conans/test/functional/layout/test_in_subfolder.py +++ b/conans/test/functional/layout/test_in_subfolder.py @@ -1,5 +1,9 @@ import textwrap +import pytest + +from conans.test.assets.cmake import gen_cmakelists +from conans.test.assets.sources import gen_function_cpp from conans.test.utils.tools import TestClient @@ -106,3 +110,60 @@ def build(self): c.run("build pkg") assert "conanfile.py (pkg/0.1): MYCMAKE-BUILD: mycmake!" in c.out assert "conanfile.py (pkg/0.1): MYUTILS-BUILD: myutils!" in c.out + + +@pytest.mark.tool_cmake +def test_exports_sources_common_code_layout(): + """ Equal to the previous test, but actually building and using cmake_layout + """ + c = TestClient() + conanfile = textwrap.dedent(""" + import os + from conan import ConanFile + from conan.tools.cmake import cmake_layout, CMake + from conan.tools.files import load, copy, save + class Pkg(ConanFile): + name = "pkg" + version = "0.1" + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeToolchain" + + def layout(self): + self.folders.root = ".." + cmake_layout(self, subfolder="pkg") + + def export_sources(self): + source_folder = os.path.join(self.recipe_folder, "..") + copy(self, "*", source_folder, self.export_sources_folder) + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + self.run(os.path.join(self.cpp.build.bindirs[0], "myapp")) + """) + cmake_include = "include(${CMAKE_CURRENT_LIST_DIR}/../common/myutils.cmake)" + c.save({"pkg/conanfile.py": conanfile, + "pkg/app.cpp": gen_function_cpp(name="main", includes=["../common/myheader"], + preprocessor=["MYDEFINE"]), + "pkg/CMakeLists.txt": gen_cmakelists(appsources=["app.cpp"], + custom_content=cmake_include), + "common/myutils.cmake": 'message(STATUS "MYUTILS.CMAKE!")', + "common/myheader.h": '#define MYDEFINE "MYDEFINEVALUE"'}) + c.run("create pkg -s compiler.version=15") + assert "MYUTILS.CMAKE!" in c.out + assert "main: Release!" in c.out + assert "MYDEFINE: MYDEFINEVALUE" in c.out + + # Local flow + c.run("install pkg -s compiler.version=15") + c.run("build pkg") + assert "MYUTILS.CMAKE!" in c.out + assert "main: Release!" in c.out + assert "MYDEFINE: MYDEFINEVALUE" in c.out + + c.run("install pkg -s compiler.version=15 -s build_type=Debug") + c.run("build pkg") + assert "MYUTILS.CMAKE!" in c.out + assert "main: Debug!" in c.out + assert "MYDEFINE: MYDEFINEVALUE" in c.out From 704586d7d54c9e704280c9e284b1cecee37fd08a Mon Sep 17 00:00:00 2001 From: memsharded Date: Fri, 8 Jul 2022 01:08:15 +0200 Subject: [PATCH 3/6] fix test --- conans/test/functional/layout/test_in_subfolder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conans/test/functional/layout/test_in_subfolder.py b/conans/test/functional/layout/test_in_subfolder.py index c984c953f35..45b8eb629bf 100644 --- a/conans/test/functional/layout/test_in_subfolder.py +++ b/conans/test/functional/layout/test_in_subfolder.py @@ -150,19 +150,19 @@ def build(self): custom_content=cmake_include), "common/myutils.cmake": 'message(STATUS "MYUTILS.CMAKE!")', "common/myheader.h": '#define MYDEFINE "MYDEFINEVALUE"'}) - c.run("create pkg -s compiler.version=15") + c.run("create pkg") assert "MYUTILS.CMAKE!" in c.out assert "main: Release!" in c.out assert "MYDEFINE: MYDEFINEVALUE" in c.out # Local flow - c.run("install pkg -s compiler.version=15") + c.run("install pkg") c.run("build pkg") assert "MYUTILS.CMAKE!" in c.out assert "main: Release!" in c.out assert "MYDEFINE: MYDEFINEVALUE" in c.out - c.run("install pkg -s compiler.version=15 -s build_type=Debug") + c.run("install pkg -s build_type=Debug") c.run("build pkg") assert "MYUTILS.CMAKE!" in c.out assert "main: Debug!" in c.out From ffb12fed88eefab39fad185f95c7b1ddf4c41286 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 11 Jul 2022 17:42:15 +0200 Subject: [PATCH 4/6] using self.folders.subproject approach --- conan/tools/cmake/layout.py | 7 +++--- conan/tools/layout/__init__.py | 6 +++-- conan/tools/microsoft/layout.py | 15 ++++++------ conans/model/layout.py | 4 ++++ .../functional/layout/test_in_subfolder.py | 3 ++- .../toolchains/gnu/test_basic_layout.py | 23 +++++++++++++++++++ .../toolchains/microsoft/test_vs_layout.py | 22 ++++++++++++++++++ 7 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 conans/test/integration/toolchains/gnu/test_basic_layout.py create mode 100644 conans/test/integration/toolchains/microsoft/test_vs_layout.py diff --git a/conan/tools/cmake/layout.py b/conan/tools/cmake/layout.py index a36429dca20..6a464f5c3e1 100644 --- a/conan/tools/cmake/layout.py +++ b/conan/tools/cmake/layout.py @@ -3,7 +3,7 @@ from conans.errors import ConanException -def cmake_layout(conanfile, generator=None, src_folder=".", subfolder=None): +def cmake_layout(conanfile, generator=None, src_folder="."): gen = conanfile.conf.get("tools.cmake.cmaketoolchain:generator", default=generator) if gen: multi = "Visual" in gen or "Xcode" in gen or "Multi-Config" in gen @@ -14,13 +14,14 @@ def cmake_layout(conanfile, generator=None, src_folder=".", subfolder=None): else: multi = False - conanfile.folders.source = src_folder if not subfolder else os.path.join(subfolder, src_folder) + subproject = conanfile.folders.subproject + conanfile.folders.source = src_folder if not subproject else os.path.join(subproject, src_folder) try: build_type = str(conanfile.settings.build_type) except ConanException: raise ConanException("'build_type' setting not defined, it is necessary for cmake_layout()") - build_folder = "build" if not subfolder else os.path.join(subfolder, "build") + build_folder = "build" if not subproject else os.path.join(subproject, "build") custom_conf = get_build_folder_custom_vars(conanfile) if custom_conf: build_folder = "{}/{}".format(build_folder, custom_conf) diff --git a/conan/tools/layout/__init__.py b/conan/tools/layout/__init__.py index 9cd97171289..8f64e14e730 100644 --- a/conan/tools/layout/__init__.py +++ b/conan/tools/layout/__init__.py @@ -6,10 +6,12 @@ def basic_layout(conanfile, src_folder="."): - conanfile.folders.build = "build" + subproject = conanfile.folders.subproject + + conanfile.folders.source = src_folder if not subproject else os.path.join(subproject, src_folder) + conanfile.folders.build = "build" if not subproject else os.path.join(subproject, "build") if conanfile.settings.get_safe("build_type"): conanfile.folders.build += "-{}".format(str(conanfile.settings.build_type).lower()) conanfile.folders.generators = os.path.join(conanfile.folders.build, "conan") conanfile.cpp.build.bindirs = ["."] conanfile.cpp.build.libdirs = ["."] - conanfile.folders.source = src_folder diff --git a/conan/tools/microsoft/layout.py b/conan/tools/microsoft/layout.py index 9d3a045e00c..4da792179e4 100644 --- a/conan/tools/microsoft/layout.py +++ b/conan/tools/microsoft/layout.py @@ -15,13 +15,14 @@ def vs_layout(conanfile): if not arch: raise ConanException("The 'vs_layout' doesn't " "work with the arch '{}'".format(conanfile.settings.arch)) - base = os.path.join(arch, str(conanfile.settings.build_type)) + bindirs = os.path.join(arch, str(conanfile.settings.build_type)) else: - base = str(conanfile.settings.build_type) + bindirs = str(conanfile.settings.build_type) - conanfile.folders.build = "." - conanfile.folders.generators = "conan" - conanfile.folders.source = "." - conanfile.cpp.build.libdirs = [base] - conanfile.cpp.build.bindirs = [base] + subproject = conanfile.folders.subproject + conanfile.folders.build = subproject or "." + conanfile.folders.generators = os.path.join(subproject, "conan") if subproject else "conan" + conanfile.folders.source = subproject or "." + conanfile.cpp.build.libdirs = [bindirs] + conanfile.cpp.build.bindirs = [bindirs] conanfile.cpp.source.includedirs = ["include"] diff --git a/conans/model/layout.py b/conans/model/layout.py index cce15ddd59f..eb3a558abf4 100644 --- a/conans/model/layout.py +++ b/conans/model/layout.py @@ -31,6 +31,10 @@ def __init__(self): # Relative location of the project root, if the conanfile is not in that project root, but # in a subfolder: e.g: If the conanfile is in a subfolder then self.root = ".." self.root = None + # The relative location wrt to the project root of the subproject containing the + # conanfile.py, that makes most of the output folders defined in layouts (cmake_layout, etc) + # start from the subproject again + self.subproject = None def __repr__(self): return str(self.__dict__) diff --git a/conans/test/functional/layout/test_in_subfolder.py b/conans/test/functional/layout/test_in_subfolder.py index 45b8eb629bf..af3f58293d9 100644 --- a/conans/test/functional/layout/test_in_subfolder.py +++ b/conans/test/functional/layout/test_in_subfolder.py @@ -130,7 +130,8 @@ class Pkg(ConanFile): def layout(self): self.folders.root = ".." - cmake_layout(self, subfolder="pkg") + self.folders.subproject = "pkg" + cmake_layout(self) def export_sources(self): source_folder = os.path.join(self.recipe_folder, "..") diff --git a/conans/test/integration/toolchains/gnu/test_basic_layout.py b/conans/test/integration/toolchains/gnu/test_basic_layout.py new file mode 100644 index 00000000000..d968008b221 --- /dev/null +++ b/conans/test/integration/toolchains/gnu/test_basic_layout.py @@ -0,0 +1,23 @@ +import os +import textwrap + +from conans.test.utils.tools import TestClient + + +def test_basic_layout_subproject(): + c = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + from conan.tools.layout import basic_layout + class Pkg(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "AutotoolsToolchain" + def layout(self): + self.folders.root = ".." + self.folders.subproject = "pkg" + basic_layout(self) + """) + c.save({"pkg/conanfile.py": conanfile}) + c.run("install pkg") + assert os.path.isfile(os.path.join(c.current_folder, "pkg", "build-release", "conan", + "conanautotoolstoolchain.bat")) diff --git a/conans/test/integration/toolchains/microsoft/test_vs_layout.py b/conans/test/integration/toolchains/microsoft/test_vs_layout.py new file mode 100644 index 00000000000..5a446da73a2 --- /dev/null +++ b/conans/test/integration/toolchains/microsoft/test_vs_layout.py @@ -0,0 +1,22 @@ +import os +import textwrap + +from conans.test.utils.tools import TestClient + + +def test_vs_layout_subproject(): + c = TestClient() + conanfile = textwrap.dedent(""" + from conan import ConanFile + from conan.tools.microsoft import vs_layout + class Pkg(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "MSBuildToolchain" + def layout(self): + self.folders.root = ".." + self.folders.subproject = "pkg" + vs_layout(self) + """) + c.save({"pkg/conanfile.py": conanfile}) + c.run("install pkg") + assert os.path.isfile(os.path.join(c.current_folder, "pkg", "conan", "conantoolchain.props")) From 0e50873af5f4169243bd87647f62e1a4a84ea069 Mon Sep 17 00:00:00 2001 From: memsharded Date: Mon, 11 Jul 2022 17:58:30 +0200 Subject: [PATCH 5/6] fix test Linux --- conans/test/integration/toolchains/gnu/test_basic_layout.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/conans/test/integration/toolchains/gnu/test_basic_layout.py b/conans/test/integration/toolchains/gnu/test_basic_layout.py index d968008b221..19cd9ebb67e 100644 --- a/conans/test/integration/toolchains/gnu/test_basic_layout.py +++ b/conans/test/integration/toolchains/gnu/test_basic_layout.py @@ -1,4 +1,5 @@ import os +import platform import textwrap from conans.test.utils.tools import TestClient @@ -19,5 +20,6 @@ def layout(self): """) c.save({"pkg/conanfile.py": conanfile}) c.run("install pkg") + ext = "sh" if platform.system() != "Windows" else "bat" assert os.path.isfile(os.path.join(c.current_folder, "pkg", "build-release", "conan", - "conanautotoolstoolchain.bat")) + "conanautotoolstoolchain.{}".format(ext))) From d748b91b30936d44ebfb4fc45cc769a9f245b7a2 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 12 Jul 2022 10:53:43 +0200 Subject: [PATCH 6/6] Update conans/model/layout.py Co-authored-by: Luis Martinez --- conans/model/layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/model/layout.py b/conans/model/layout.py index eb3a558abf4..d3c4828cb65 100644 --- a/conans/model/layout.py +++ b/conans/model/layout.py @@ -31,7 +31,7 @@ def __init__(self): # Relative location of the project root, if the conanfile is not in that project root, but # in a subfolder: e.g: If the conanfile is in a subfolder then self.root = ".." self.root = None - # The relative location wrt to the project root of the subproject containing the + # The relative location with respect to the project root of the subproject containing the # conanfile.py, that makes most of the output folders defined in layouts (cmake_layout, etc) # start from the subproject again self.subproject = None