Repository: conan-io/conan
Pull request #18498 (2025-06-19 11:47:19)

Added architecture linker specific flags to toolchains
Changelog: Feature: new linker flags autodetected by conan based on profile architecture
Docs: omit

In https://github.com/conan-io/conan/pull/18432 native `asmjs` architecture support was introduced. 
To enable `asmjs` code generation the `-sWASM=0` flag needs to be passed to the `emscripten linker`. 

That flag was added in `architecture_flag` without noticing that those flags were being passed to both compiler and linker across all toolchains.
This was causing the compiler to throw warnings as this flag is only understood by the linker:

```
em++: warning: linker setting ignored during compilation: 'WASM' [-Wunused-command-line-argument]
```
Having a compilation warning is not desirable as a compilation with warning as errors will result in a build failure:

```
em++: error: linker setting ignored during compilation: 'WASM' [-Wunused-command-line-argument] [-Werror]
```

This PR aims to address this issue by creating a new `architecture_link_flags` which will return the required flags **only** for the linker based on the profile architecture.

This function will be called by several toolchains which will bypass the result to the final linker by setting `LD_FLAGS`.

#### Tests

The current [`test_emcc`](https://github.com/conan-io/conan/blob/92b606909bd4fe04f5b3ea515130d31a8f0481da/test/functional/toolchains/emscripten/test_emcc.py) test suite already tests this new functionality added.



Patch:
diff --git a/conan/tools/build/flags.py b/conan/tools/build/flags.py
index 441aca0af..49156c5ed 100644
--- a/conan/tools/build/flags.py
+++ b/conan/tools/build/flags.py
@@ -67,11 +67,21 @@ def architecture_flag(conanfile):
                 "e2k-v6": "-march=elbrus-v6",
                 "e2k-v7": "-march=elbrus-v7"}.get(arch, "")
     elif compiler == "emcc":
-        # Emscripten default output is WASM since 1.37.x (long time ago)
         if arch == "wasm64":
             return "-sMEMORY64=1"
+    return ""
+
+
+def architecture_link_flag(conanfile):
+    """
+    returns exclusively linker flags specific to the target architecture and compiler
+    """
+    compiler = conanfile.settings.get_safe("compiler")
+    arch = conanfile.settings.get_safe("arch")
+    if compiler == "emcc":
+        # Emscripten default output is WASM since 1.37.x (long time ago)
         # Deactivate WASM output forcing asm.js output instead
-        elif arch == "asm.js":
+        if arch == "asm.js":
             return "-sWASM=0"
     return ""
 
diff --git a/conan/tools/cmake/toolchain/blocks.py b/conan/tools/cmake/toolchain/blocks.py
index 2df118716..d2a77fda6 100644
--- a/conan/tools/cmake/toolchain/blocks.py
+++ b/conan/tools/cmake/toolchain/blocks.py
@@ -10,7 +10,7 @@ from conan.tools.apple.apple import get_apple_sdk_fullname, _to_apple_arch
 from conan.tools.android.utils import android_abi
 from conan.tools.apple.apple import is_apple_os, to_apple_arch
 from conan.tools.build import build_jobs
-from conan.tools.build.flags import architecture_flag, libcxx_flags
+from conan.tools.build.flags import architecture_flag, architecture_link_flag, libcxx_flags
 from conan.tools.build.cross_building import cross_building
 from conan.tools.cmake.toolchain import CONAN_TOOLCHAIN_FILENAME
 from conan.tools.cmake.utils import is_multi_configuration
@@ -238,19 +238,26 @@ class SkipRPath(Block):
 class ArchitectureBlock(Block):
     template = textwrap.dedent("""\
         # Define C++ flags, C flags and linker flags from 'settings.arch'
-
+        {% if arch_flag %}
         message(STATUS "Conan toolchain: Defining architecture flag: {{ arch_flag }}")
         string(APPEND CONAN_CXX_FLAGS " {{ arch_flag }}")
         string(APPEND CONAN_C_FLAGS " {{ arch_flag }}")
         string(APPEND CONAN_SHARED_LINKER_FLAGS " {{ arch_flag }}")
         string(APPEND CONAN_EXE_LINKER_FLAGS " {{ arch_flag }}")
+        {% endif %}
+        {% if arch_link_flag %}
+        message(STATUS "Conan toolchain: Defining architecture linker flag: {{ arch_link_flag }}")
+        string(APPEND CONAN_SHARED_LINKER_FLAGS " {{ arch_link_flag }}")
+        string(APPEND CONAN_EXE_LINKER_FLAGS " {{ arch_link_flag }}")
+        {% endif %}
         """)
 
     def context(self):
         arch_flag = architecture_flag(self._conanfile)
-        if not arch_flag:
+        arch_link_flag = architecture_link_flag(self._conanfile)
+        if not arch_flag and not arch_link_flag:
             return
-        return {"arch_flag": arch_flag}
+        return {"arch_flag": arch_flag, "arch_link_flag": arch_link_flag}
 
 
 class LinkerScriptsBlock(Block):
diff --git a/conan/tools/gnu/autotoolstoolchain.py b/conan/tools/gnu/autotoolstoolchain.py
index bb3168df4..1b2ecd3c9 100644
--- a/conan/tools/gnu/autotoolstoolchain.py
+++ b/conan/tools/gnu/autotoolstoolchain.py
@@ -6,7 +6,7 @@ from conan.internal.internal_tools import raise_on_universal_arch
 from conan.tools.apple.apple import is_apple_os, resolve_apple_flags, apple_extra_flags
 from conan.tools.build import cmd_args_to_string, save_toolchain_args
 from conan.tools.build.cross_building import cross_building
-from conan.tools.build.flags import architecture_flag, build_type_flags, cppstd_flag, \
+from conan.tools.build.flags import architecture_flag, architecture_link_flag, build_type_flags, cppstd_flag, \
     build_type_link_flags, libcxx_flags, cstd_flag, llvm_clang_front
 from conan.tools.env import Environment, VirtualBuildEnv
 from conan.tools.gnu.get_gnu_triplet import _get_gnu_triplet
@@ -52,6 +52,7 @@ class AutotoolsToolchain:
         self.cppstd = cppstd_flag(self._conanfile)
         self.cstd = cstd_flag(self._conanfile)
         self.arch_flag = architecture_flag(self._conanfile)
+        self.arch_ld_flag = architecture_link_flag(self._conanfile)
         self.libcxx, self.gcc_cxx11_abi = libcxx_flags(self._conanfile)
         self.fpic = self._conanfile.options.get_safe("fPIC")
         self.msvc_runtime_flag = self._get_msvc_runtime_flag()
@@ -223,7 +224,7 @@ class AutotoolsToolchain:
 
     @property
     def ldflags(self):
-        ret = [self.arch_flag, self.sysroot_flag]
+        ret = [self.arch_flag, self.sysroot_flag, self.arch_ld_flag]
         apple_flags = [self.apple_isysroot_flag, self.apple_arch_flag, self.apple_min_version_flag]
         apple_flags += self.apple_extra_flags
         conf_flags = self._conanfile.conf.get("tools.build:sharedlinkflags", default=[],
diff --git a/conan/tools/gnu/gnutoolchain.py b/conan/tools/gnu/gnutoolchain.py
index d7cd0a384..3a478d962 100644
--- a/conan/tools/gnu/gnutoolchain.py
+++ b/conan/tools/gnu/gnutoolchain.py
@@ -5,7 +5,7 @@ from conan.internal.internal_tools import raise_on_universal_arch
 from conan.tools.apple.apple import is_apple_os, resolve_apple_flags, apple_extra_flags
 from conan.tools.build import cmd_args_to_string, save_toolchain_args
 from conan.tools.build.cross_building import cross_building
-from conan.tools.build.flags import architecture_flag, build_type_flags, cppstd_flag, \
+from conan.tools.build.flags import architecture_flag, architecture_link_flag, build_type_flags, cppstd_flag, \
     build_type_link_flags, \
     libcxx_flags, llvm_clang_front
 from conan.tools.env import Environment, VirtualBuildEnv
@@ -57,6 +57,7 @@ class GnuToolchain:
 
         self.cppstd = cppstd_flag(self._conanfile)
         self.arch_flag = architecture_flag(self._conanfile)
+        self.arch_ld_flag = architecture_link_flag(self._conanfile)
         self.libcxx, self.gcc_cxx11_abi = libcxx_flags(self._conanfile)
         self.fpic = self._conanfile.options.get_safe("fPIC")
         self.msvc_runtime_flag = self._get_msvc_runtime_flag()
@@ -288,7 +289,7 @@ class GnuToolchain:
 
     @property
     def ldflags(self):
-        ret = [self.arch_flag, self.sysroot_flag]
+        ret = [self.arch_flag, self.sysroot_flag, self.arch_ld_flag]
         apple_flags = [self.apple_isysroot_flag, self.apple_arch_flag, self.apple_min_version_flag]
         apple_flags += self.apple_extra_flags
         conf_flags = self._conanfile.conf.get("tools.build:sharedlinkflags", default=[],
diff --git a/conan/tools/meson/toolchain.py b/conan/tools/meson/toolchain.py
index 8dd569ee4..c1f1e79ed 100644
--- a/conan/tools/meson/toolchain.py
+++ b/conan/tools/meson/toolchain.py
@@ -9,7 +9,7 @@ from conan.internal.internal_tools import raise_on_universal_arch
 from conan.tools.apple.apple import is_apple_os, apple_min_version_flag, \
     resolve_apple_flags, apple_extra_flags
 from conan.tools.build.cross_building import cross_building
-from conan.tools.build.flags import libcxx_flags, architecture_flag
+from conan.tools.build.flags import architecture_link_flag, libcxx_flags, architecture_flag
 from conan.tools.env import VirtualBuildEnv
 from conan.tools.meson.helpers import *
 from conan.tools.meson.helpers import get_apple_subsystem
@@ -216,6 +216,8 @@ class MesonToolchain:
         self.extra_defines = []
         #: Architecture flag deduced by Conan and added to ``c_args``, ``cpp_args``, ``c_link_args`` and ``cpp_link_args``
         self.arch_flag = architecture_flag(self._conanfile)  # https://github.com/conan-io/conan/issues/17624
+        #: Architecture link flag deduced by Conan and added to ``c_link_args`` and ``cpp_link_args``
+        self.arch_link_flag = architecture_link_flag(self._conanfile)
         #: Dict-like object that defines Meson ``properties`` with ``key=value`` format
         self.properties = {}
         #: Dict-like object that defines Meson ``project options`` with ``key=value`` format
@@ -459,7 +461,7 @@ class MesonToolchain:
         return {
             "cxxflags": [self.arch_flag] + cxxflags + sys_root + self.extra_cxxflags,
             "cflags": [self.arch_flag] + cflags + sys_root + self.extra_cflags,
-            "ldflags": [self.arch_flag] + ld,
+            "ldflags": [self.arch_flag] + [self.arch_link_flag] + ld,
             "defines": [f"-D{d}" for d in (defines + self.extra_defines)]
         }
 


Test patch:
diff --git a/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py b/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
index 7576aa6dd..ca6dcde78 100644
--- a/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
+++ b/test/unittests/client/toolchain/autotools/autotools_toolchain_test.py
@@ -285,6 +285,27 @@ def test_architecture_flag(config):
     assert expected in env["LDFLAGS"]
     assert "-debug" not in env["LDFLAGS"]
 
+@pytest.mark.parametrize("config", [
+    ("gcc", "x86_64", ""),
+    ("emcc", "wasm", ""),
+    ("emcc", "wasm64", ""),
+    ("emcc", "asm.js", "-sWASM=0")])
+def test_architecture_link_flag(config):
+    compiler, arch, expected = config
+    conanfile = ConanFileMock()
+    conanfile.settings = MockSettings(
+        {"build_type": "Release",
+         "os": "Emscripten",
+         "compiler": compiler,
+         "arch": arch})
+    conanfile.settings_build = conanfile.settings
+    be = AutotoolsToolchain(conanfile)
+    assert be.arch_ld_flag == expected
+    env = be.vars()
+    assert "" in env["CXXFLAGS"]
+    assert "" in env["CFLAGS"]
+    assert expected in env["LDFLAGS"]
+
 
 @pytest.mark.parametrize("compiler", ['msvc'])
 def test_build_type_flag(compiler):