diff --git a/conan/cli/commands/build.py b/conan/cli/commands/build.py index aa62a740d99..4720e6259a1 100644 --- a/conan/cli/commands/build.py +++ b/conan/cli/commands/build.py @@ -61,7 +61,6 @@ def build(conan_api, parser, *args): print_graph_packages(deps_graph) out = ConanOutput() - out.title("Installing packages") conan_api.install.install_binaries(deps_graph=deps_graph, remotes=remotes) out.title("Finalizing install (deploy, generators)") diff --git a/conans/model/build_info.py b/conans/model/build_info.py index ca4d00e36ae..e721fab1bf4 100644 --- a/conans/model/build_info.py +++ b/conans/model/build_info.py @@ -445,7 +445,12 @@ def merge_list(o, d): if other._properties: current_values = self.get_init("_properties", {}) - current_values.update(other._properties) + for k, v in other._properties.items(): + existing = current_values.get(k) + if existing is not None and isinstance(existing, list) and not overwrite: + existing.extend(v) + else: + current_values[k] = v def set_relative_base_folder(self, folder): for varname in _DIRS_VAR_NAMES: diff --git a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py index 8b7774f243b..27981e025d4 100644 --- a/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py +++ b/test/functional/toolchains/cmake/cmakedeps/test_cmakedeps_build_modules.py @@ -204,3 +204,174 @@ def package_info(self): client.save({"CMakeLists.txt": cmakelists.replace("crypto", "#crypto")}) assert "ROOT MESSAGE:hello!" not in client.out + +@pytest.mark.tool("cmake") +@pytest.mark.parametrize("editable", [True, False]) +def test_build_modules_custom_script_editable(editable): + c = TestClient() + conanfile = textwrap.dedent(r""" + import os, glob + from conan import ConanFile + from conan.tools.cmake import cmake_layout + from conan.tools.files import copy, save + + class Conan(ConanFile): + name = "myfunctions" + version = "1.0" + exports_sources = ["src/*.cmake"] + settings = "build_type", "arch" + + def build(self): + cmake = 'set(MY_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR})\n'\ + 'macro(otherfunc)\n'\ + 'file(READ "${MY_CMAKE_PATH}/my.txt" c)\n'\ + 'message("Hello ${c}!!!!")\nendmacro()' + save(self, "otherfuncs.cmake", cmake) + save(self, "my.txt", "contents of text file!!!!") + + def layout(self): + cmake_layout(self, src_folder="src") + src = glob.glob(os.path.join(self.recipe_folder, self.folders.source, "*.cmake")) + build = glob.glob(os.path.join(self.recipe_folder, self.folders.build, "*.cmake")) + self.cpp.source.set_property("cmake_build_modules", src) + self.cpp.build.set_property("cmake_build_modules", build) + + def package(self): + copy(self, "*.cmake", self.source_folder, os.path.join(self.package_folder, "mods"), + keep_path=False) + copy(self, "*.cmake", self.build_folder, os.path.join(self.package_folder, "mods"), + keep_path=False) + copy(self, "*.txt", self.build_folder, os.path.join(self.package_folder, "mods"), + keep_path=False) + + def package_info(self): + self.cpp_info.set_property("cmake_build_modules", glob.glob("mods/*.cmake")) + """) + + myfunction = textwrap.dedent(""" + function(myfunction) + message("Hello myfunction!!!!") + endfunction() + """) + + consumer = textwrap.dedent(""" + from conan import ConanFile + from conan.tools.cmake import CMake + + class Conan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeToolchain", "CMakeDeps" + requires = "myfunctions/1.0" + + def build(self): + cmake = CMake(self) + cmake.configure() + """) + cmakelists = textwrap.dedent(""" + cmake_minimum_required(VERSION 3.15) + project(test) + find_package(myfunctions CONFIG REQUIRED) + myfunction() + otherfunc() + """) + c.save({"functions/conanfile.py": conanfile, + "functions/src/myfunction.cmake": myfunction, + "app/conanfile.py": consumer, + "app/CMakeLists.txt": cmakelists}) + + if editable: + c.run("editable add functions") + c.run('build functions -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"') + else: + c.run("create functions") + c.run('build app -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"') + assert "Hello myfunction!!!!" in c.out + assert "Hello contents of text file!!!!" in c.out + + +@pytest.mark.tool("cmake") +@pytest.mark.parametrize("editable", [True, False]) +def test_build_modules_custom_script_editable_package(editable): + # same as above, but with files from the package folder + c = TestClient() + conanfile = textwrap.dedent(r""" + import os, glob + from conan import ConanFile + from conan.tools.cmake import cmake_layout + from conan.tools.files import copy, save + + class Conan(ConanFile): + name = "myfunctions" + version = "1.0" + exports_sources = ["src/*.cmake", "src/vis/*.vis"] + settings = "build_type", "arch" + + def build(self): + config = str(self.settings.build_type).upper() + cmake = f'set(MY_CMAKE_PATH ${{{self.name}_RES_DIRS_{config}}})\n'\ + 'macro(otherfunc)\n'\ + 'file(READ "${MY_CMAKE_PATH}/my.vis" c)\n'\ + 'message("Hello ${c}!!!!")\nendmacro()' + save(self, "otherfuncs.cmake", cmake) + + def layout(self): + cmake_layout(self, src_folder="src") + src = glob.glob(os.path.join(self.recipe_folder, self.folders.source, "*.cmake")) + build = glob.glob(os.path.join(self.recipe_folder, self.folders.build, "*.cmake")) + self.cpp.source.set_property("cmake_build_modules", src) + self.cpp.build.set_property("cmake_build_modules", build) + self.cpp.source.resdirs = ["vis"] + + def package(self): + copy(self, "*.cmake", self.source_folder, os.path.join(self.package_folder, "mods"), + keep_path=False) + copy(self, "*.cmake", self.build_folder, os.path.join(self.package_folder, "mods"), + keep_path=False) + copy(self, "*.vis", self.source_folder, os.path.join(self.package_folder, "mods"), + keep_path=False) + + def package_info(self): + self.cpp_info.resdirs = ["mods"] + self.cpp_info.set_property("cmake_build_modules", glob.glob("mods/*.cmake")) + """) + + myfunction = textwrap.dedent(""" + function(myfunction) + message("Hello myfunction!!!!") + endfunction() + """) + + consumer = textwrap.dedent(""" + from conan import ConanFile + from conan.tools.cmake import CMake + + class Conan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeToolchain", "CMakeDeps" + requires = "myfunctions/1.0" + + def build(self): + cmake = CMake(self) + cmake.configure() + """) + cmakelists = textwrap.dedent(""" + cmake_minimum_required(VERSION 3.15) + project(test) + find_package(myfunctions CONFIG REQUIRED) + myfunction() + otherfunc() + """) + c.save({"functions/conanfile.py": conanfile, + "functions/src/myfunction.cmake": myfunction, + "functions/src/vis/my.vis": "contents of text file!!!!", + "app/conanfile.py": consumer, + "app/CMakeLists.txt": cmakelists}) + + if editable: + c.run("editable add functions") + c.run('build functions -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"') + else: + c.run("create functions -vv") + c.run('build app -c tools.cmake.cmake_layout:build_folder_vars="[\'settings.arch\']"') + assert "Hello myfunction!!!!" in c.out + assert "Hello contents of text file!!!!" in c.out