Repository: conan-io/conan

Issue:
Incorrect value for conanfile_directory when being packaged in the build method
The problem is that the conanfile is read just once, the first time it is needed, from the "export" folder, and thus, the initial value for `ConanFile._conanfile_directory` is initialized to that value.

Afterwards, when it is used in the `build()` method, it is still pointing to the "export" folder, not the current "build" folder, which has a copy of the "export" folder + possibly cloned or retrieved code with `source()` method.

This feature is tested, but it passes just by chance, as reading the CMakeLists and code from the export folder also works in the examples, but there are many other cases in which it will fail. 

It can be possibly fixed by adding a setter and making conan set it to the correct folder when calling `build()`, but I will analyze if there are other possible interactions with this issue. 

cc/ @skizzay 



Discussion:

Resolving patch:
diff --git a/conans/client/installer.py b/conans/client/installer.py
--- a/conans/client/installer.py
+++ b/conans/client/installer.py
@@ -292,6 +292,9 @@ def _build_package(self, export_folder, src_folder, build_folder, conan_file, ou
         # Read generators from conanfile and generate the needed files
         write_generators(conan_file, build_folder, output)
         try:
+            # This is necessary because it is different for user projects
+            # than for packages
+            conan_file._conanfile_directory = build_folder
             conan_file.build()
             self._out.writeln("")
             output.success("Package '%s' built" % os.path.basename(build_folder))
diff --git a/conans/client/packager.py b/conans/client/packager.py
--- a/conans/client/packager.py
+++ b/conans/client/packager.py
@@ -30,6 +30,7 @@ def new_method(pattern, src=""):
     conanfile.copy_bins = wrap(DEFAULT_BIN)
     conanfile.copy_res = wrap(DEFAULT_RES)
     try:
+        conanfile.package_folder = package_folder
         conanfile.package()
         conanfile.copy.execute()
     except Exception as e:


Test patch:
diff --git a/conans/test/command/export_test.py b/conans/test/command/export_test.py
--- a/conans/test/command/export_test.py
+++ b/conans/test/command/export_test.py
@@ -34,7 +34,7 @@ def test_basic(self):
         expected_sums = {'hello.cpp': '4f005274b2fdb25e6113b69774dac184',
                          'main.cpp': '0479f3c223c9a656a718f3148e044124',
                          'CMakeLists.txt': 'bc3405da4bb0b51a3b9f05aca71e58c8',
-                         'conanfile.py': '79cdacbd40569a842fcd3ae5085b3cbf',
+                         'conanfile.py': '644b60e95962dabec1a814dedea32874',
                          'helloHello0.h': '9448df034392fc8781a47dd03ae71bdd'}
         self.assertEqual(expected_sums, manif.file_sums)
 
@@ -106,7 +106,7 @@ def test_export_the_same_code(self):
         expected_sums = {'hello.cpp': '4f005274b2fdb25e6113b69774dac184',
                          'main.cpp': '0479f3c223c9a656a718f3148e044124',
                          'CMakeLists.txt': 'bc3405da4bb0b51a3b9f05aca71e58c8',
-                         'conanfile.py': '79cdacbd40569a842fcd3ae5085b3cbf',
+                         'conanfile.py': '644b60e95962dabec1a814dedea32874',
                          'helloHello0.h': '9448df034392fc8781a47dd03ae71bdd'}
         self.assertEqual(expected_sums, digest2.file_sums)
 
@@ -139,7 +139,7 @@ def test_export_a_new_version(self):
         expected_sums = {'hello.cpp': '4f005274b2fdb25e6113b69774dac184',
                          'main.cpp': '0479f3c223c9a656a718f3148e044124',
                          'CMakeLists.txt': 'bc3405da4bb0b51a3b9f05aca71e58c8',
-                         'conanfile.py': 'a816517b2ae7642d09d4a8a75b7f4563',
+                         'conanfile.py': '833d37426fe82042ddaf343c26a34ec8',
                          'helloHello0.h': '9448df034392fc8781a47dd03ae71bdd'}
         self.assertEqual(expected_sums, digest3.file_sums)
 
diff --git a/conans/test/integration/complete_test.py b/conans/test/integration/complete_test.py
--- a/conans/test/integration/complete_test.py
+++ b/conans/test/integration/complete_test.py
@@ -20,7 +20,7 @@ def setUp(self):
 
     def reuse_test(self):
         conan_reference = ConanFileReference.loads("Hello0/0.1@lasote/stable")
-        files = cpp_hello_conan_files("Hello0", "0.1")
+        files = cpp_hello_conan_files("Hello0", "0.1", need_patch=True)
 
         self.client.save(files)
         self.client.run("export lasote/stable")
diff --git a/conans/test/integration/diamond_test.py b/conans/test/integration/diamond_test.py
--- a/conans/test/integration/diamond_test.py
+++ b/conans/test/integration/diamond_test.py
@@ -20,7 +20,7 @@ def setUp(self):
         self.conan = TestClient(servers=self.servers, users=[("lasote", "mypass")])
 
     def _export_upload(self, name, version=None, deps=None):
-        files = cpp_hello_conan_files(name, version, deps)
+        files = cpp_hello_conan_files(name, version, deps, need_patch=True)
         conan_ref = ConanFileReference(name, version, "lasote", "stable")
         self.conan.save(files, clean_first=True)
         self.conan.run("export lasote/stable")
diff --git a/conans/test/utils/cpp_test_files.py b/conans/test/utils/cpp_test_files.py
--- a/conans/test/utils/cpp_test_files.py
+++ b/conans/test/utils/cpp_test_files.py
@@ -3,6 +3,7 @@
 
 conanfile_template = """
 from conans import ConanFile, CMake
+from conans.tools import replace_in_file
 import platform
 
 class {name}Conan(ConanFile):
@@ -21,6 +22,17 @@ def config(self):
         for name, req in self.requires.iteritems():
             self.options[name].language = self.options.language
 
+    def source(self):
+        # Try-except necessary, not all tests have all files
+        try:
+            replace_in_file("CMakeLists.txt", "projct", "project")
+        except:
+            pass
+        try:
+            replace_in_file("main.cpp", "retunr", "return")
+        except:
+            pass
+
     def build(self):
         static_flags = "-DBUILD_SHARED_LIBS=ON" if not self.options.static else ""
         lang = '-DCONAN_LANGUAGE=%s' % self.options.language
@@ -99,7 +111,7 @@ def imports(self):
 
 
 def cpp_hello_source_files(name="Hello", deps=None, private_includes=False,
-                           msg=None, dll_export=False):
+                           msg=None, dll_export=False, need_patch=False):
     """
     param number: integer, defining name of the conans Hello0, Hello1, HelloX
     param deps: [] list of integers, defining which dependencies this conans
@@ -110,6 +122,12 @@ def cpp_hello_source_files(name="Hello", deps=None, private_includes=False,
                by default
     param dll_export: Adds __declspec(dllexport) to the .h declaration
                       (to be exported to lib with a dll)
+    param need_patch: It will generated wrong CMakeLists and main.cpp files,
+                      so they will need to be fixed/patched in the source() method. 
+                      Such method just have to replace_in_file in those two files to have a
+                      correct "source" directory. This was introduced to be sure that the
+                      source and build methods are executed using their respective folders
+                      while packaging.
     e.g. (3, [4, 7]) means that a Hello3 conans will be created, with message
          "Hello 3", that depends both in Hello4 and Hello7.
          The output of such a conans exe could be like: Hello 3, Hello 4, Hello7
@@ -134,11 +152,14 @@ def cpp_hello_source_files(name="Hello", deps=None, private_includes=False,
 
     # Naive approximation, NO DEPS
     ret["CMakeLists.txt"] = cmake_file.format(name=name)
+    if need_patch:
+        ret["CMakeLists.txt"] = ret["CMakeLists.txt"].replace("project", "projct")
+        ret["main.cpp"] = ret["main.cpp"].replace("return", "retunr")
     return ret
 
 
 def cpp_hello_conan_files(name="Hello", version="0.1", deps=None, language=0, static=True,
-                          private_includes=False, msg=None, dll_export=False):
+                          private_includes=False, msg=None, dll_export=False, need_patch=False):
     """Generate hello_files, as described above, plus the necessary
     CONANFILE to manage it
     param number: integer, defining name of the conans Hello0, Hello1, HelloX
@@ -169,7 +190,7 @@ def cpp_hello_conan_files(name="Hello", version="0.1", deps=None, language=0, st
     requires = ", ".join(requires)
 
     base_files = cpp_hello_source_files(name, code_deps, private_includes, msg=msg,
-                                        dll_export=dll_export)
+                                        dll_export=dll_export, need_patch=need_patch)
     conanfile = conanfile_template.format(name=name,
                                       version=version,
                                       requires=requires,