Repository: conan-io/conan
Pull request #18377 (2025-05-28 03:00:31)

Fix Conan Source folders.root issue
Close #18376

Changelog: (Bugfix): Fixes issue where conan source() method doesn't use folders.root when present
Docs: https://docs.conan.io/2/reference/conanfile/methods/layout.html#self-folders

No doc update should be needed as this implements the expected behavior for folders.root.


CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/conan-io/conan?pullRequest=18377) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/conan-io/conan?pullRequest=18377) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/conan-io/conan?pullRequest=18377) it.</sub>

Patch:
diff --git a/conan/api/subapi/local.py b/conan/api/subapi/local.py
index d8b2ef386..11c6d5c1f 100644
--- a/conan/api/subapi/local.py
+++ b/conan/api/subapi/local.py
@@ -82,7 +82,9 @@ class LocalAPI:
             with conanfile_exception_formatter(conanfile, "layout"):
                 conanfile.layout()
 
-        folder = conanfile.recipe_folder
+        folder = conanfile.recipe_folder if conanfile.folders.root is None else \
+            os.path.normpath(os.path.join(conanfile.recipe_folder, conanfile.folders.root))
+
         conanfile.folders.set_base_source(folder)
         conanfile.folders.set_base_export_sources(folder)
         conanfile.folders.set_base_recipe_metadata(os.path.join(folder, "metadata"))


Test patch:
diff --git a/test/integration/command/source_test.py b/test/integration/command/source_test.py
index 89cd5372f..92fbc708e 100644
--- a/test/integration/command/source_test.py
+++ b/test/integration/command/source_test.py
@@ -142,6 +142,39 @@ class ConanLib(ConanFile):
         self.assertIn("conanfile.py: Running source!", client.out)
         self.assertEqual("Hello World", client.load("file1.txt"))
 
+    def test_local_source_layout_root(self):
+        # Ensure that if a root folder is specified Conan source method
+        # uses it. This is especially important when running with
+        # no_copy_source where both the build()  and source() methods
+        # need to reference source_folder (so the value must be the
+        # same in both methods)
+        conanfile = textwrap.dedent('''
+            from conan import ConanFile
+            from conan.tools.files import save, load
+
+            class ConanLib(ConanFile):
+                
+                def layout(self):
+                    self.folders.root = ".."
+                    self.folders.source = "src"
+                
+                def source(self):
+                    self.output.info(f"In the source() method the Source folder is: {self.source_folder}")
+                    save(self, "source_file.c", "conent")
+
+                def build(self):
+                    self.output.info(f"In the build() method the Source folder is: {self.source_folder}")
+                    load(self, f"{self.source_folder}/source_file.c")
+            ''')
+        client = TestClient(light=True)
+        client.save({CONANFILE: conanfile})
+        
+        client.run("source .")
+        # The build method will fail if source() and build() don't 
+        # both use the correct source_folder
+        client.run("build .")
+        assert "conanfile.py: Calling build()" in client.out  # doesn't fail
+
     def test_retrieve_exports_sources(self):
         # For Conan 2.0 if we install a package from a remote and we want to upload to other
         # remote we need to download the sources, as we consider revisions immutable, let's