Skip to content

Commit

Permalink
Feature/relativize generators (#13607)
Browse files Browse the repository at this point in the history
* wip

* wip

* relative_generators

* clean

* fixes

* fix test

* fix test to pass in linux/mac

* fix Mac arm

* merged develop2
  • Loading branch information
memsharded authored Apr 5, 2023
1 parent 5bb2b8d commit c98183e
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 43 deletions.
5 changes: 4 additions & 1 deletion conan/tools/cmake/cmakedeps/cmakedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from conan.tools.cmake.cmakedeps.templates.target_configuration import TargetConfigurationTemplate
from conan.tools.cmake.cmakedeps.templates.target_data import ConfigDataTemplate
from conan.tools.cmake.cmakedeps.templates.targets import TargetsTemplate
from conans.client.generators import relativize_generated_file
from conans.errors import ConanException
from conans.util.files import save

Expand Down Expand Up @@ -94,7 +95,9 @@ def _generate_files(self, require, dep, ret, find_module_mode):
ret[config_version.filename] = config_version.render()

data_target = ConfigDataTemplate(self, require, dep, find_module_mode)
ret[data_target.filename] = data_target.render()
data_content = relativize_generated_file(data_target.render(), self._conanfile,
"${CMAKE_CURRENT_LIST_DIR}")
ret[data_target.filename] = data_content

target_configuration = TargetConfigurationTemplate(self, require, dep, find_module_mode)
ret[target_configuration.filename] = target_configuration.render()
Expand Down
8 changes: 1 addition & 7 deletions conan/tools/cmake/cmakedeps/templates/target_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ def context(self):

# Make the root_folder relative to the generated xxx-data.cmake file
root_folder = self._root_folder
generators_folder = self.cmakedeps._conanfile.generators_folder
if os.path.commonpath([root_folder, generators_folder]) == generators_folder:
rel_path = os.path.relpath(root_folder, generators_folder)
rel_path = rel_path.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')
root_folder = f"${{CMAKE_CURRENT_LIST_DIR}}/{rel_path}"
else:
root_folder = root_folder.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')
root_folder = root_folder.replace('\\', '/').replace('$', '\\$').replace('"', '\\"')

return {"global_cpp": global_cpp,
"has_components": self.conanfile.cpp_info.has_components,
Expand Down
9 changes: 3 additions & 6 deletions conan/tools/cmake/toolchain/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,12 +520,9 @@ def context(self):
pkg_config = self._conanfile.conf.get("tools.gnu:pkg_config", check_type=str)
if pkg_config:
pkg_config = pkg_config.replace("\\", "/")
pkg_config_path = self._conanfile.generators_folder
if pkg_config_path:
# hardcoding scope as "build"
subsystem = deduce_subsystem(self._conanfile, "build")
pathsep = ":" if subsystem != WINDOWS else ";"
pkg_config_path = pkg_config_path.replace("\\", "/") + pathsep
subsystem = deduce_subsystem(self._conanfile, "build")
pathsep = ":" if subsystem != WINDOWS else ";"
pkg_config_path = "${CMAKE_CURRENT_LIST_DIR}" + pathsep
return {"pkg_config": pkg_config,
"pkg_config_path": pkg_config_path}

Expand Down
2 changes: 2 additions & 0 deletions conan/tools/cmake/toolchain/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from conan.tools.intel import IntelCC
from conan.tools.microsoft import VCVars
from conan.tools.microsoft.visual import vs_ide_version
from conans.client.generators import relativize_generated_file
from conans.errors import ConanException
from conans.model.options import _PackageOption
from conans.util.files import save
Expand Down Expand Up @@ -171,6 +172,7 @@ def _context(self):
def content(self):
context = self._context()
content = Template(self._template, trim_blocks=True, lstrip_blocks=True).render(**context)
content = relativize_generated_file(content, self._conanfile, "${CMAKE_CURRENT_LIST_DIR}")
return content

def generate(self):
Expand Down
4 changes: 2 additions & 2 deletions conan/tools/env/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from collections import OrderedDict
from contextlib import contextmanager

from conans.client.generators import relativize_generated_file
from conans.client.subsystems import deduce_subsystem, WINDOWS, subsystem_path
from conans.errors import ConanException
from conans.model.recipe_ref import ref_matches
Expand Down Expand Up @@ -414,14 +415,13 @@ def save_bat(self, file_location, generate_deactivate=True):
{deactivate}
""").format(deactivate=deactivate if generate_deactivate else "")
result = [capture]
location = os.path.abspath(os.path.dirname(file_location))
for varname, varvalues in self._values.items():
value = varvalues.get_str("%{name}%", subsystem=self._subsystem, pathsep=self._pathsep)
# To make the script relocatable
value = value.replace(location, "%~dp0")
result.append('set "{}={}"'.format(varname, value))

content = "\n".join(result)
content = relativize_generated_file(content, self._conanfile, "%~dp0")
# It is very important to save it correctly with utf-8, the Conan util save() is broken
os.makedirs(os.path.dirname(os.path.abspath(file_location)), exist_ok=True)
open(file_location, "w", encoding="utf-8").write(content)
Expand Down
12 changes: 12 additions & 0 deletions conans/client/generators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,15 @@ def ps1_content(files):
if generated:
conanfile.output.highlight("Generating aggregated env files")
conanfile.output.info(f"Generated aggregated env files: {generated}")


def relativize_generated_file(content, conanfile, placeholder):
abs_base_path = conanfile.folders._base_generators
if not abs_base_path or not os.path.isabs(abs_base_path):
return content
generators_folder = conanfile.generators_folder
rel_path = os.path.relpath(abs_base_path, generators_folder)
new_path = placeholder if rel_path == "." else os.path.join(placeholder, rel_path)
content = content.replace(abs_base_path, new_path)
content = content.replace(abs_base_path.replace("\\", "/"), new_path.replace("\\", "/"))
return content
105 changes: 85 additions & 20 deletions conans/test/functional/command/test_install_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,47 @@
from conans.util.files import save


@pytest.mark.tool("cmake")
def test_install_deploy():
@pytest.fixture(scope="module")
def _client():
c = TestClient()
c.run("new cmake_lib -d name=hello -d version=0.1")
c.run("create . -o *:shared=True -tf=")
conanfile = textwrap.dedent("""
import os
from conan import ConanFile
from conan.tools.files import save
class Tool(ConanFile):
name = "tool"
version = "1.0"
def package(self):
save(self, os.path.join(self.package_folder, "build", "my_tools.cmake"),
'set(MY_TOOL_VARIABLE "Hello world!")')
def package_info(self):
self.cpp_info.includedirs = []
self.cpp_info.libdirs = []
self.cpp_info.bindirs = []
path_build_modules = os.path.join("build", "my_tools.cmake")
self.cpp_info.set_property("cmake_build_modules", [path_build_modules])
""")
import os
from conan import ConanFile
from conan.tools.files import save
class Tool(ConanFile):
name = "tool"
version = "1.0"
def package(self):
save(self, os.path.join(self.package_folder, "build", "my_tools.cmake"),
'set(MY_TOOL_VARIABLE "Hello world!")')
def package_info(self):
self.cpp_info.includedirs = []
self.cpp_info.libdirs = []
self.cpp_info.bindirs = []
path_build_modules = os.path.join("build", "my_tools.cmake")
self.cpp_info.set_property("cmake_build_modules", [path_build_modules])
""")
c.save({"conanfile.py": conanfile}, clean_first=True)
c.run("create .")
return c


@pytest.fixture()
def client(_client):
""" this is much faster than creating and uploading everythin
"""
client = TestClient(default_server_user=True)
shutil.rmtree(client.cache_folder)
shutil.copytree(_client.cache_folder, client.cache_folder)
return client


@pytest.mark.tool("cmake")
def test_install_deploy(client):
c = client
custom_content = 'message(STATUS "MY_TOOL_VARIABLE=${MY_TOOL_VARIABLE}!")'
cmake = gen_cmakelists(appname="my_app", appsources=["main.cpp"], find_package=["hello", "tool"],
custom_content=custom_content)
Expand All @@ -59,7 +75,6 @@ def deploy(graph, output_folder, **kwargs):
clean_first=True)
c.run("install . -o *:shared=True "
"--deploy=deploy.py -of=mydeploy -g CMakeToolchain -g CMakeDeps")
print(c.out)
c.run("remove * -c") # Make sure the cache is clean, no deps there
arch = c.get_default_host_profile().settings['arch']
deps = c.load(f"mydeploy/hello-release-{arch}-data.cmake")
Expand All @@ -84,6 +99,56 @@ def deploy(graph, output_folder, **kwargs):
assert "hello/0.1: Hello World Release!" in c2.out


@pytest.mark.tool("cmake")
def test_install_full_deploy_layout(client):
c = client
custom_content = 'message(STATUS "MY_TOOL_VARIABLE=${MY_TOOL_VARIABLE}!")'
cmake = gen_cmakelists(appname="my_app", appsources=["main.cpp"], find_package=["hello", "tool"],
custom_content=custom_content)
conanfile = textwrap.dedent("""
[requires]
hello/0.1
tool/1.0
[generators]
CMakeDeps
CMakeToolchain
[layout]
cmake_layout
""")
c.save({"conanfile.txt": conanfile,
"CMakeLists.txt": cmake,
"main.cpp": gen_function_cpp(name="main", includes=["hello"], calls=["hello"])},
clean_first=True)
c.run("install . -o *:shared=True --deploy=full_deploy.py")
c.run("remove * -c") # Make sure the cache is clean, no deps there
arch = c.get_default_host_profile().settings['arch']
folder = "/Release" if platform.system() != "Windows" else ""
rel_path = "../../" if platform.system() == "Windows" else "../../../"
deps = c.load(f"build{folder}/generators/hello-release-{arch}-data.cmake")
assert 'set(hello_PACKAGE_FOLDER_RELEASE "${CMAKE_CURRENT_LIST_DIR}/' \
f'{rel_path}full_deploy/host/hello/0.1/Release/{arch}")' in deps
assert 'set(hello_INCLUDE_DIRS_RELEASE "${hello_PACKAGE_FOLDER_RELEASE}/include")' in deps
assert 'set(hello_LIB_DIRS_RELEASE "${hello_PACKAGE_FOLDER_RELEASE}/lib")' in deps

# We can fully move it to another folder, and still works
tmp = os.path.join(temp_folder(), "relocated")
shutil.copytree(c.current_folder, tmp)
shutil.rmtree(c.current_folder)
c2 = TestClient(current_folder=tmp)
with c2.chdir(f"build{folder}"):
# I can totally build without errors with deployed
cmakelist = "../.." if platform.system() != "Windows" else ".."
c2.run_command(f"cmake {cmakelist} -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake "
"-DCMAKE_BUILD_TYPE=Release")
assert "MY_TOOL_VARIABLE=Hello world!!" in c2.out
c2.run_command("cmake --build . --config Release")
if platform.system() == "Windows": # Only the .bat env-generators are relocatable atm
cmd = r"generators\conanrun.bat && Release\my_app.exe"
# For Lunux: cmd = ". mydeploy/conanrun.sh && ./my_app"
c2.run_command(cmd)
assert "hello/0.1: Hello World Release!" in c2.out


def test_copy_files_deploy():
c = TestClient()
deploy = textwrap.dedent("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ def test_pkg_config_block():
assert 'set(PKG_CONFIG_EXECUTABLE /usr/local/bin/pkg-config CACHE FILEPATH ' in toolchain
pathsep = ":" if os_ != "Windows" else ";"
pkg_config_path_set = 'set(ENV{PKG_CONFIG_PATH} "%s$ENV{PKG_CONFIG_PATH}")' % \
(client.current_folder.replace("\\", "/") + pathsep)
("${CMAKE_CURRENT_LIST_DIR}" + pathsep)
assert pkg_config_path_set in toolchain


Expand Down
8 changes: 4 additions & 4 deletions conans/test/unittests/tools/cmake/test_cmakedeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def test_cpp_info_name_cmakedeps():
"arch": ["x86"]})
conanfile.settings.build_type = "Release"
conanfile.settings.arch = "x86"
conanfile.folders.set_base_generators("/")
conanfile.folders.set_base_generators("/some/abs/path") # non-existing to not relativize

cpp_info = CppInfo(set_defaults=True)
cpp_info.set_property("cmake_target_name", "MySuperPkg1::MySuperPkg1")
Expand Down Expand Up @@ -63,7 +63,7 @@ def test_cpp_info_name_cmakedeps_components():
"arch": ["x86", "x64"]})
conanfile.settings.build_type = "Debug"
conanfile.settings.arch = "x64"
conanfile.folders.set_base_generators("/")
conanfile.folders.set_base_generators("/some/abs/path") # non-existing to not relativize

cpp_info = CppInfo()
cpp_info.set_property("cmake_file_name", "ComplexFileName1")
Expand Down Expand Up @@ -113,7 +113,7 @@ def test_cmake_deps_links_flags():
"arch": ["x86"]})
conanfile.settings.build_type = "Release"
conanfile.settings.arch = "x86"
conanfile.folders.set_base_generators("/")
conanfile.folders.set_base_generators("/some/abs/path") # non-existing to not relativize

cpp_info = CppInfo()
# https://github.com/conan-io/conan/issues/8811 regression, fix with explicit - instead of /
Expand Down Expand Up @@ -161,7 +161,7 @@ def test_component_name_same_package():
"arch": ["x86"]})
conanfile.settings.build_type = "Release"
conanfile.settings.arch = "x86"
conanfile.folders.set_base_generators("/")
conanfile.folders.set_base_generators("/some/abs/path") # non-existing to not relativize

cpp_info = CppInfo(set_defaults=True)

Expand Down
4 changes: 2 additions & 2 deletions conans/test/unittests/tools/cmake/test_cmaketoolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def conanfile():
c.settings.os = "Windows"
c.conf = Conf()
c.conf.define("tools.cmake.cmaketoolchain:system_name", "potato")
c.folders.set_base_generators(".")
c.folders.set_base_generators("/some/abs/path") # non-existing to not relativize
c._conan_node = Mock()
c._conan_node.transitive_deps = {}
return c
Expand Down Expand Up @@ -146,7 +146,7 @@ def conanfile_apple():
c.settings.os.version = "10.15"
c.settings_build = c.settings
c.conf = Conf()
c.folders.set_base_generators(".")
c.folders.set_base_generators("/some/abs/path") # non-existing to not relativize
c._conan_node = Mock()
c._conan_node.dependencies = []
c._conan_node.transitive_deps = {}
Expand Down

0 comments on commit c98183e

Please sign in to comment.