Repository: conan-io/conan

Issue:
b2 generator is broken for projects that doesn't use compiler setting
A consuming project might not have compiler in its settings. In such case
b2 generator creates files that put `None-None` toolset into usage
requirements of dependencies. If those dependencies are then used in the consuming project's build scripts an error occurs, because there's no toolset `None-None`.

Example of a project that would trigger such error:

```python
# conanfile.py
from conans import ConanFile

class FoobarConan(ConanFile):
    name = 'foobar'
    version = '0.0.1'
    requires = 'Catch2/[>=2.0]@catchorg/stable'
    build_requires = 'b2/4.0.0'

    generators = 'b2'
    exports_sources = 'jamroot.jam'

    def build(self):
        self.run("b2")
```

```jam
# jamroot.jam
project foobar ;

alias foobar : /Catch2//libs ;
```

Relevant for Conan 1.18.5


- [x] I've read the [CONTRIBUTING guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've specified the Conan version, operating system version and any tool that can be relevant.
- [x] I've explained the steps to reproduce the error or the motivation/use case of the question/suggestion.


Discussion:

Resolving patch:
diff --git a/conans/client/generators/b2.py b/conans/client/generators/b2.py
--- a/conans/client/generators/b2.py
+++ b/conans/client/generators/b2.py
@@ -214,7 +214,7 @@ def b2_variation(self):
         """
         if not getattr(self, "_b2_variation_key", None):
             self._b2_variation = {}
-            self._b2_variation['toolset'] = self.b2_toolset_name + '-' + self.b2_toolset_version
+            self._b2_variation['toolset'] = self.b2_toolset
             self._b2_variation['architecture'] = {
                 'x86': 'x86', 'x86_64': 'x86',
                 'ppc64le': 'power', 'ppc64': 'power', 'ppc32': 'power',
@@ -279,7 +279,7 @@ def b2_variation(self):
         return self._b2_variation
 
     @property
-    def b2_toolset_name(self):
+    def b2_toolset(self):
         compiler = {
             'sun-cc': 'sun',
             'gcc': 'gcc',
@@ -287,16 +287,17 @@ def b2_toolset_name(self):
             'clang': 'clang',
             'apple-clang': 'clang'
         }.get(self.conanfile.settings.get_safe('compiler'))
-        return str(compiler)
+        if not compiler:
+            return
 
-    @property
-    def b2_toolset_version(self):
-        if self.conanfile.settings.get_safe('compiler') == 'Visual Studio':
+        if compiler == 'msvc':
             if self.conanfile.settings.compiler.version == '15':
-                return '14.1'
+                version = '14.1'
             else:
-                return str(self.conanfile.settings.compiler.version)+'.0'
-        return str(self.conanfile.settings.get_safe('compiler.version'))
+                version = str(self.conanfile.settings.compiler.version)+'.0'
+        else:
+            version = str(self.conanfile.settings.get_safe('compiler.version'))
+        return compiler + '-' + version
 
     conanbuildinfo_header_text = """\
 #|


Test patch:
diff --git a/conans/test/unittests/client/generators/b2_test.py b/conans/test/unittests/client/generators/b2_test.py
--- a/conans/test/unittests/client/generators/b2_test.py
+++ b/conans/test/unittests/client/generators/b2_test.py
@@ -49,8 +49,31 @@ def b2_test(self):
         generator = B2Generator(conanfile)
 
         content = {
-'conanbuildinfo.jam':
-'''#|
+            'conanbuildinfo.jam': _main_buildinfo_full,
+            'conanbuildinfo-316f2f0b155dc874a672d40d98d93f95.jam':
+                _variation_full,
+        }
+
+        for ck, cv in generator.content.items():
+            self.assertEqual(cv, content[ck])
+
+    def b2_empty_settings_test(self):
+        conanfile = ConanFile(TestBufferConanOutput(), None)
+        conanfile.initialize(Settings({}), EnvValues())
+
+        generator = B2Generator(conanfile)
+
+        content = {
+            'conanbuildinfo.jam': _main_buildinfo_empty,
+            'conanbuildinfo-d41d8cd98f00b204e9800998ecf8427e.jam':
+                _variation_empty,
+        }
+
+        for ck, cv in generator.content.items():
+            self.assertEqual(cv, content[ck])
+
+_main_buildinfo_full = '''\
+#|
     B2 definitions for Conan packages. This is a generated file.
     Edit the corresponding conanfile.txt instead.
 |#
@@ -154,9 +177,10 @@ def b2_test(self):
         call-in-project : include-conanbuildinfo $(__cbi__) ;
     }
 }
-''',
-'conanbuildinfo-316f2f0b155dc874a672d40d98d93f95.jam':
-'''#|
+'''
+
+_variation_full = '''\
+#|
     B2 definitions for Conan packages. This is a generated file.
     Edit the corresponding conanfile.txt instead.
 |#
@@ -328,16 +352,121 @@ def b2_test(self):
         :
         : $(usage-requirements(mypkg2,32,x86,17,gnu,linux,gcc-6.3,release)) ;
     call-in-project $(mypkg2-mod) : explicit libs ; }
-''',
-        }
+'''
 
-        for ck, cv in generator.content.items():
-            self.assertEqual(cv, content[ck])
+_main_buildinfo_empty = '''\
+#|
+    B2 definitions for Conan packages. This is a generated file.
+    Edit the corresponding conanfile.txt instead.
+|#
 
-    def b2_empty_settings_test(self):
-        conanfile = ConanFile(TestBufferConanOutput(), None)
-        conanfile.initialize(Settings({}), EnvValues())
+import path ;
+import project ;
+import modules ;
+import feature ;
 
-        generator = B2Generator(conanfile)
-        # fails if generator doesn't support empty settings
-        generator.content
+local base-project = [ project.current ] ;
+local base-project-mod = [ $(base-project).project-module ] ;
+local base-project-location = [ project.attribute $(base-project-mod) location ] ;
+
+rule project-define ( id )
+{
+    id = $(id:L) ;
+    local saved-project = [ modules.peek project : .base-project ] ;
+    local id-location = [ path.join $(base-project-location) $(id) ] ;
+    local id-mod = [ project.load $(id-location) : synthesize ] ;
+    project.initialize $(id-mod) : $(id-location) ;
+    project.inherit-attributes $(id-mod) : $(base-project-mod) ;
+    local attributes = [ project.attributes $(id-mod) ] ;
+    $(attributes).set parent-module : $(base-project-mod) : exact ;
+    modules.poke $(base-project-mod) : $(id)-mod : $(id-mod) ;
+    modules.poke [ CALLER_MODULE ] : $(id)-mod : $(id-mod) ;
+    modules.poke project : .base-project : $(saved-project) ;
+    IMPORT $(__name__)
+        : constant-if call-in-project
+        : $(id-mod)
+        : constant-if call-in-project ;
+    if [ project.is-jamroot-module $(base-project-mod) ]
+    {
+        use-project /$(id) : $(id) ;
+    }
+    return $(id-mod) ;
+}
+
+rule constant-if ( name : value * )
+{
+    if $(__define_constants__) && $(value)
+    {
+        call-in-project : constant $(name) : $(value) ;
+        modules.poke $(__name__) : $(name) : [ modules.peek $(base-project-mod) : $(name) ] ;
+    }
+}
+
+rule call-in-project ( project-mod ? : rule-name args * : * )
+{
+    project-mod ?= $(base-project-mod) ;
+    project.push-current [ project.target $(project-mod) ] ;
+    local result = [ modules.call-in $(project-mod) :
+        $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) : $(10) :
+        $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : $(18) :
+        $(19) ] ;
+    project.pop-current ;
+    return $(result) ;
+}
+
+rule include-conanbuildinfo ( cbi )
+{
+    include $(cbi) ;
+}
+
+IMPORT $(__name__)
+    : project-define constant-if call-in-project include-conanbuildinfo
+    : $(base-project-mod)
+    : project-define constant-if call-in-project include-conanbuildinfo ;
+
+if ! ( relwithdebinfo in [ feature.values variant ] )
+{
+    variant relwithdebinfo : : <optimization>speed <debug-symbols>on <inlining>full <runtime-debugging>off ;
+}
+if ! ( minsizerel in [ feature.values variant ] )
+{
+    variant minsizerel : : <optimization>space <debug-symbols>off <inlining>full <runtime-debugging>off ;
+}
+
+local __conanbuildinfo__ = [ GLOB $(__file__:D) : conanbuildinfo-*.jam : downcase ] ;
+{
+    local __define_constants__ = yes ;
+    for local __cbi__ in $(__conanbuildinfo__)
+    {
+        call-in-project : include-conanbuildinfo $(__cbi__) ;
+    }
+}
+
+{
+    local __define_targets__ = yes ;
+    for local __cbi__ in $(__conanbuildinfo__)
+    {
+        call-in-project : include-conanbuildinfo $(__cbi__) ;
+    }
+}
+'''
+
+_variation_empty = '''\
+#|
+    B2 definitions for Conan packages. This is a generated file.
+    Edit the corresponding conanfile.txt instead.
+|#
+
+# global
+constant-if rootpath(conan,) :
+    ""
+    ;
+
+constant-if usage-requirements(conan,) :
+    <include>$(includedirs(conan,))
+    <define>$(defines(conan,))
+    <cflags>$(cflags(conan,))
+    <cxxflags>$(cppflags(conan,))
+    <link>shared:<linkflags>$(sharedlinkflags(conan,))
+    ;
+'''