Repository: conan-io/conan

Issue:
What causes error message: ERROR: <pkg-name> 'settings.arch' value not defined?
Conan 1.6.1

Hi,

We have a structure like the following (simplified):

PkgA requires PkgB
PkgB requires PkgC

PkgA has no settings attribute
PkgB has _settings = "os", "compiler", "build_type", "arch"_
PkgC has no settings attribute

if we run (for PkgA):
```
conan install PkgA-recipe -if <build dir>
conan build PkgA-recipe -bf <build dir> -sf <src folder>
conan export-pkg PkgA-recipe dev/test -bf <build dir>
```

we get the error _ERROR: PkgB 'settings.arch' value not defined_. The output from conan suggests that the _conan export-pkg_ command successfully exporting the package, but failed before the package() method is called.

if we run (for PkgA):
`conan create PkgA-recipe dev/test`

it works fine.

To stop the error for the local build we added _settings = "os", "compiler", "build_type", "arch"_ to PkgA but we don't understand why this was required.

Any guidance on what we may have done wrong?


Discussion:
It might be a bug, thanks for reporting, we are going to investigate.
Reproduced, certainly unexpected behavior, labeling as bug and trying to fix it for 1.7
Hi @keithrob91 

I have been working on this, but is more complex than expected. Will continue working for 1.8, but don't have time in conan 1.7. 

In the meantime, please use one of the following workarounds:
- Add the ``settings`` to the header only recipe, as you did. Remember to add ``self.info.header_only()`` to the ``package_id()`` of your header only recipe conanfile.py
- Use a normal ``conan create`` to package the header only. It will be very fast too, and the recipe will be extremely simple. ``conan export-pkg`` actually only makes full sense if you are creating binaries outside of the conan-cache, but for header only it is not a thing.
OK, thank you for update. We've gone for the first option
hi

i came across an issue with `conan info` failing with a package having the `cmake_installer` as build_requires:

> $ conan info ..
> ERROR: cmake_installer/3.12.2@myuser/mychannel: 'settings.arch_build' value not defined

Of course `arch_build` is defined in the settings.yml and set correctly in the default profile to `x86_64`. Installing the `cmake_installer/3.12.2@myuser/mychannel` package incl. the correct binaries works as well.
`conan info -s arch_build=x86_64 ..` also works though `arch_build=x86_64` is already set in the default profile:

> $ conan profile get settings.arch_build default
> x86_64

Could this be caused by the same bug? Is there a possible workaround?

Resolving patch:
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -398,14 +398,8 @@ def export_pkg(self, conanfile_path, name, channel, source_folder=None, build_fo
         source_folder = _make_abs_path(source_folder, cwd, default=os.path.dirname(conanfile_path))
 
         # Checks that no both settings and info files are specified
-        if install_folder and existing_info_files(install_folder) and \
-                (profile_name or settings or options or env):
-            raise ConanException("%s and %s are found, at '%s' folder, so specifying profile, "
-                                 "settings, options or env is not allowed" % (CONANINFO, BUILD_INFO,
-                                                                              install_folder))
-
         infos_present = existing_info_files(install_folder)
-        if not infos_present:
+        if profile_name or settings or options or env or not infos_present:
             profile = profile_from_args(profile_name, settings, options, env=env,
                                         cwd=cwd, client_cache=self._client_cache)
         else:
diff --git a/conans/client/packager.py b/conans/client/packager.py
--- a/conans/client/packager.py
+++ b/conans/client/packager.py
@@ -5,7 +5,8 @@
 from conans.util.files import mkdir, save, rmdir
 from conans.util.log import logger
 from conans.paths import CONANINFO
-from conans.errors import ConanException, ConanExceptionInUserConanfileMethod, conanfile_exception_formatter
+from conans.errors import (ConanException, ConanExceptionInUserConanfileMethod,
+                           conanfile_exception_formatter)
 from conans.model.manifest import FileTreeManifest
 from conans.client.output import ScopedOutput
 from conans.client.file_copier import FileCopier
@@ -17,7 +18,6 @@ def export_pkg(conanfile, pkg_id, src_package_folder, package_folder, output, pl
     conanfile.package_folder = src_package_folder
     output.info("Exporting to cache existing package from user folder")
     output.info("Package folder %s" % package_folder)
-    print("export_pkg", type(reference))
     plugin_manager.execute("pre_package", conanfile=conanfile, conanfile_path=conanfile_path,
                            reference=reference, package_id=pkg_id)
 


Test patch:
diff --git a/conans/test/command/export_pkg_test.py b/conans/test/command/export_pkg_test.py
--- a/conans/test/command/export_pkg_test.py
+++ b/conans/test/command/export_pkg_test.py
@@ -29,6 +29,36 @@ class Pkg(ConanFile):
         client.run("install .")
         client.run("export-pkg . Pkg/0.1@user/testing")
 
+    def test_transitive_without_settings(self):
+        # https://github.com/conan-io/conan/issues/3367
+        conanfile = """from conans import ConanFile
+class PkgC(ConanFile):
+    pass
+"""
+        client = TestClient()
+        client.save({CONANFILE: conanfile})
+        client.run("create . PkgC/0.1@user/testing")
+        conanfile = """from conans import ConanFile
+class PkgB(ConanFile):
+    settings = "arch"
+    requires = "PkgC/0.1@user/testing"
+"""
+        client.save({CONANFILE: conanfile})
+        client.run("create . PkgB/0.1@user/testing")
+        conanfile = """from conans import ConanFile
+class PkgA(ConanFile):
+    requires = "PkgB/0.1@user/testing"
+    def build(self):
+        self.output.info("BUILDING PKGA")
+"""
+        client.save({CONANFILE: conanfile})
+        client.run("install . -if=build")
+        client.run("build . -bf=build")
+        client.run("export-pkg . PkgA/0.1@user/testing -bf=build -pr=default")
+        self.assertIn("PkgA/0.1@user/testing: Package "
+                      "'8f97510bcea8206c1c046cc8d71cc395d4146547' created",
+                      client.out)
+
     def test_package_folder_errors(self):
         # https://github.com/conan-io/conan/issues/2350
         conanfile = """from conans import ConanFile
@@ -179,45 +209,42 @@ def package(self):
                       client.user_io.out)
 
         # Now repeat
-        client.save({CONANFILE: conanfile}, clean_first=True)
-        client.save({"include/header.h": "//Windows header2"})
+        client.save({CONANFILE: conanfile,
+                     "include/header.h": "//Windows header2"}, clean_first=True)
+        # Without force it fails
         err = client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows",
                          ignore_error=True)
         self.assertIn("Package already exists. Please use --force, -f to overwrite it",
                       client.user_io.out)
         self.assertTrue(err)
-
-        # Will fail because it finds the info files in the curdir.
-        client.run("install . -s os=Windows")
-        err = client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows -f", ignore_error=True)
-        self.assertTrue(err)
-        self.assertIn("conaninfo.txt and conanbuildinfo.txt are found", client.out)
-
-        client.save({CONANFILE: conanfile, "include/header.h": "//Windows header2"},
-                    clean_first=True)
+        # With force works
         client.run("export-pkg . Hello/0.1@lasote/stable -s os=Windows -f")
         self.assertEqual(load(os.path.join(package_folder, "include/header.h")),
                          "//Windows header2")
 
-        # Now use --install-folder and avoid the -s os=Windows, should fail without  -f
+        # Now use --install-folder
+        client.save({CONANFILE: conanfile,
+                     "include/header.h": "//Windows header3"}, clean_first=True)
+        # Without force it fails
         client.run("install . --install-folder=inst -s os=Windows")
         err = client.run("export-pkg . Hello/0.1@lasote/stable -if inst", ignore_error=True)
         self.assertTrue(err)
         self.assertIn("Package already exists. Please use --force, -f to overwrite it",
                       client.user_io.out)
+        # With force works
         client.run("export-pkg . Hello/0.1@lasote/stable -if inst -f")
+        self.assertIn("Hello/0.1@lasote/stable: Package '3475bd55b91ae904ac96fde0f106a136ab951a5e'"
+                      " created", client.out)
         self.assertEqual(load(os.path.join(package_folder, "include/header.h")),
-                         "//Windows header2")
-
-        # Try to specify a setting and the install folder
-        error = client.run("export-pkg . Hello/0.1@lasote/stable -if inst -s os=Linux", ignore_error=True)
-        self.assertTrue(error)
-        self.assertIn("conaninfo.txt and conanbuildinfo.txt are found", client.user_io.out)
+                         "//Windows header3")
 
-        error = client.run("export-pkg . Hello/0.1@lasote/stable -if inst -f --profile=default",
-                           ignore_error=True)
-        self.assertIn("conaninfo.txt and conanbuildinfo.txt are found", client.user_io.out)
-        self.assertTrue(error)
+        # we can specify settings too
+        client.save({"include/header.h": "//Windows header4"})
+        client.run("export-pkg . Hello/0.1@lasote/stable -if inst -f -s os=Windows")
+        self.assertIn("Hello/0.1@lasote/stable: Package '3475bd55b91ae904ac96fde0f106a136ab951a5e'"
+                      " created", client.out)
+        self.assertEqual(load(os.path.join(package_folder, "include/header.h")),
+                         "//Windows header4")
 
         # Try to specify a install folder with no files
         error = client.run("export-pkg . Hello/0.1@lasote/stable -if fake", ignore_error=True)
@@ -376,7 +403,8 @@ def package(self):
         # Partial reference is ok
         client.save({CONANFILE: conanfile, "file.txt": "txt contents"})
         client.run("export-pkg . anyname/1.222@conan/stable")
-        self.assertIn("anyname/1.222@conan/stable package(): Copied 1 '.txt' file: file.txt", client.out)
+        self.assertIn("anyname/1.222@conan/stable package(): Copied 1 '.txt' file: file.txt",
+                      client.out)
 
     def test_with_deps(self):
         client = TestClient()