Skip to content

Commit

Permalink
proposal for source() buildenv discussion (#15153)
Browse files Browse the repository at this point in the history
* proposal for source() buildenv discussion

* wip, failing

* trying alternative opt-in class attribute

* fix

* wip

* wip

* add cmake_layout test

* wip

* named auto_generate
  • Loading branch information
memsharded authored Dec 14, 2023
1 parent 0843372 commit 9f023c3
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 20 deletions.
2 changes: 2 additions & 0 deletions conan/api/subapi/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def source(self, path, name=None, version=None, user=None, channel=None, remotes
conanfile.folders.set_base_source(folder)
conanfile.folders.set_base_export_sources(folder)
conanfile.folders.set_base_recipe_metadata(os.path.join(folder, "metadata"))
# The generators are needed for the "conan source" local case with tool-requires
conanfile.folders.set_base_generators(folder)
conanfile.folders.set_base_build(None)
conanfile.folders.set_base_package(None)

Expand Down
33 changes: 20 additions & 13 deletions conan/tools/env/virtualbuildenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,32 @@ class VirtualBuildEnv:
.bat or .sh script
"""

def __init__(self, conanfile):
def __init__(self, conanfile, auto_generate=False):
self._conanfile = conanfile
self._conanfile.virtualbuildenv = False
if not auto_generate:
self._conanfile.virtualbuildenv = False
self.basename = "conanbuildenv"
# TODO: Make this use the settings_build
self.configuration = conanfile.settings.get_safe("build_type")
if self.configuration:
self.configuration = self.configuration.lower()
self.arch = conanfile.settings.get_safe("arch")
if self.arch:
self.arch = self.arch.lower()
self.configuration = None
self.arch = None

@property
def _filename(self):
if not self.configuration:
# TODO: Make this use the settings_build
configuration = self._conanfile.settings.get_safe("build_type")
configuration = configuration.lower() if configuration else None
else:
configuration = self.configuration
if not self.arch:
arch = self._conanfile.settings.get_safe("arch")
arch = arch.lower() if arch else None
else:
arch = self.arch
f = self.basename
if self.configuration:
f += "-" + self.configuration.replace(".", "_")
if self.arch:
f += "-" + self.arch.replace(".", "_")
if configuration:
f += "-" + configuration.replace(".", "_")
if arch:
f += "-" + arch.replace(".", "_")
return f

def environment(self):
Expand Down
6 changes: 1 addition & 5 deletions conan/tools/meson/toolchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,7 @@ def __init__(self, conanfile, backend=None):
compilers_by_conf = self._conanfile.conf.get("tools.build:compiler_executables", default={},
check_type=dict)
# Read the VirtualBuildEnv to update the variables
# FIXME: This VirtualBuildEnv instance is breaking things!!
# FIXME: It shouldn't be used here, not intended for this use case
prev_status = self._conanfile.virtualbuildenv
build_env = VirtualBuildEnv(self._conanfile).vars()
self._conanfile.virtualbuildenv = prev_status
build_env = VirtualBuildEnv(self._conanfile, auto_generate=True).vars()
#: Sets the Meson ``c`` variable, defaulting to the ``CC`` build environment value.
#: If provided as a blank-separated string, it will be transformed into a list.
#: Otherwise, it remains a single string.
Expand Down
8 changes: 6 additions & 2 deletions conans/client/source.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os

from conan.api.output import ConanOutput
from conan.tools.env import VirtualBuildEnv
from conans.errors import ConanException, conanfile_exception_formatter, NotFoundException, \
conanfile_remove_attr
from conans.util.files import (is_dirty, mkdir, rmdir, set_dirty_context_manager,
Expand Down Expand Up @@ -67,8 +68,11 @@ def config_source(export_source_folder, conanfile, hook_manager):
# First of all get the exported scm sources (if auto) or clone (if fixed)
# Now move the export-sources to the right location
merge_directories(export_source_folder, conanfile.folders.base_source)

run_source_method(conanfile, hook_manager)
if getattr(conanfile, "source_buildenv", True):
with VirtualBuildEnv(conanfile, auto_generate=True).vars().apply():
run_source_method(conanfile, hook_manager)
else:
run_source_method(conanfile, hook_manager)


def run_source_method(conanfile, hook_manager):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ def test_tool_requires_revision_profile():
# We shoul be able to explicitly [tool_require] a recipe revision in the profile
c = TestClient()
build_profile = textwrap.dedent("""\
[settings]
os=Linux
[tool_requires]
*:tool/0.1#2d65f1b4af1ce59028f96adbfe7ed5a2
""")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import textwrap

import pytest

from conans.test.utils.tools import TestClient


class TestBuildEnvSource:
@pytest.fixture()
def client(self):
c = TestClient()
tool = textwrap.dedent(r"""
import os
from conan import ConanFile
from conan.tools.files import chdir, save
class Tool(ConanFile):
name = "tool"
version = "0.1"
def package(self):
with chdir(self, self.package_folder):
echo = f"@echo off\necho MY-TOOL! {self.name}/{self.version}!!"
save(self, "bin/mytool.bat", echo)
save(self, "bin/mytool.sh", echo)
os.chmod("bin/mytool.sh", 0o777)
""")
c.save({"conanfile.py": tool})
c.run("create .")
return c

def test_source_buildenv(self, client):
c = client
pkg = textwrap.dedent("""
from conan import ConanFile
import platform
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
tool_requires = "tool/0.1"
def source(self):
cmd = "mytool.bat" if platform.system() == "Windows" else "mytool.sh"
self.run(cmd)
""")
c.save({"conanfile.py": pkg})
c.run("create .")
assert "MY-TOOL! tool/0.1" in c.out

c.run("install .") # to generate conanbuild script first, so it is available
c.run("source .")
assert "MY-TOOL! tool/0.1" in c.out

def test_source_buildenv_layout(self, client):
c = client
pkg = textwrap.dedent("""
from conan import ConanFile
import platform
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
tool_requires = "tool/0.1"
settings = "build_type"
def layout(self):
self.folders.source = "mysrc"
bt = self.settings.get_safe("build_type") or "Release"
self.folders.generators = f"mybuild{bt}"
def source(self):
cmd = "mytool.bat" if platform.system() == "Windows" else "mytool.sh"
self.run(cmd)
""")
c.save({"conanfile.py": pkg})
c.run("create .")
assert "MY-TOOL! tool/0.1" in c.out

c.run("install .") # to generate conanbuild script first, so it is available
# But they are in a different folder, user can copy them to source folder to make
# them available to source() method. This works
# shutil.copytree(os.path.join(c.current_folder, "mybuild"),
# os.path.join(c.current_folder, "mysrc"))
# Another possibility is user directly calling the "conanbuild" script to activate
# The current solution defines "generators" to be robust for "conan source" command
# defaulting to "Release" config
c.run("source .")
assert "MY-TOOL! tool/0.1" in c.out

def test_source_buildenv_cmake_layout(self, client):
c = client
pkg = textwrap.dedent("""
import os
from conan import ConanFile
from conan.tools.cmake import cmake_layout
import platform
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
tool_requires = "tool/0.1"
settings = "build_type"
def layout(self):
cmake_layout(self)
bt = self.settings.get_safe("build_type") or "Release"
self.folders.generators = os.path.join(bt, "generators")
def source(self):
cmd = "mytool.bat" if platform.system() == "Windows" else "mytool.sh"
self.run(cmd)
""")
c.save({"conanfile.py": pkg})
c.run("create .")
assert "MY-TOOL! tool/0.1" in c.out

c.run("install .")
c.run("source .")
assert "MY-TOOL! tool/0.1" in c.out

def test_source_buildenv_optout(self, client):
c = client

pkg = textwrap.dedent("""
from conan import ConanFile
import platform
class Pkg(ConanFile):
name = "pkg"
version = "0.1"
tool_requires = "tool/0.1"
source_buildenv = False
def source(self):
cmd = "mytool.bat" if platform.system() == "Windows" else "mytool.sh"
self.run(cmd)
""")
c.save({"conanfile.py": pkg})
c.run("create .", assert_error=True)
assert "ERROR: pkg/0.1: Error in source() method, line 14" in c.out

# Local will still work, because ``install`` generates env-scripts and no layout
c.run("install .")
c.run("source .")
assert "MY-TOOL! tool/0.1" in c.out

0 comments on commit 9f023c3

Please sign in to comment.