Skip to content

toolchain: the target's convention pin silently overrides an explicitly-set default toolchain, and toolchain list still reports the ignored one #283

Description

@Sunrisepeak

Setting a default and then building disagree about which toolchain is in effect, with nothing said either way.

$ mcpp toolchain default gcc 15 --target x86_64-linux-musl
     Default set to gcc@15.1.0 → x86_64-linux-musl (was: gcc@16.1.0)

$ mcpp toolchain list
  *  gcc 15.1.0              (default)
  *  x86_64-linux-musl       static      gcc 15.1.0    installed

$ mcpp build
    Resolved gcc@16.1.0 → x86_64-linux-musl → .../xim-x-musl-gcc/16.1.0/bin/x86_64-linux-musl-g++

Reproduced on 0.0.107 with ~/.mcpp/config.toml holding [toolchain] default = "gcc@15.1.0", default_target = "x86_64-linux-musl", and both musl-gcc 15.1.0 and 16.1.0 installed.

Cause

Once the target triple is known, the vocabulary tables convention pin replaces whatever tcSpec held — src/build/prepare.cppm:894:

if (known && !hasToolchainOverride && !known->pin.empty())
    tcSpec = std::string(known->pin);

x86_64-linux-musl is pinned to gcc@16.1.0 (src/toolchain/triple.cppm:104), and hasToolchainOverride (prepare.cppm:854) is true only for a [target.X] toolchain = ... section in the projects mcpp.toml:

bool hasToolchainOverride = hasExplicitSection && !it->second.toolchain.empty();

The global config is not consulted — and the global config is exactly what mcpp toolchain default writes (lifecycle.cppm:690). So a users explicit choice never counts as an override and the pin always wins.

mcpp toolchain list reads the config directly instead (effective_default_toolchain, lifecycle.cppm:198/223), which is why it shows the value the build just discarded. Two code paths, two different notions of "the default".

Why it matters

The pin itself is well-founded — the comment in triple.cppm records that GCC 15 drops module template instantiations at link under musl — so silently preferring 16 is defensible. What is not defensible is doing it without a word while toolchain list presents the ignored setting as the default. A user has no way to tell the setting did not take, and --print-fingerprint / mcpp explain toolchain are not where they would look after running an explicit toolchain default.

Note the pins own comment (triple.cppm:93-95) scopes it to "--target <canonical> with no explicit ... override". Someone who ran mcpp toolchain default gcc 15 --target x86_64-linux-musl has made an explicit choice; the code just does not recognise that spelling of it.

Workaround

A per-project override does take effect:

[target.x86_64-linux-musl]
toolchain = "gcc@15.1.0"

Resolved gcc@15.1.0 → x86_64-linux-musl → .../xim-x-musl-gcc/15.1.0/bin/x86_64-linux-musl-g++ (verified).

Directions

  1. Let an explicitly-set global default carry the same weight as [target.X] toolchain — the user asked for it by name, including the target.
  2. If the pin must still win (module-linking correctness is a real constraint), say so at resolve time: note: x86_64-linux-musl pins gcc@16.1.0; your default gcc@15.1.0 was not used — and have toolchain list mark the pinned value as what will actually run.
  3. Either way the two paths should share one resolution function, so list and build cannot drift again.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions