Skip to content

Record what an executable was built with, alongside the executable? #1238

Description

@jgabry

Note: While working on #1235 I was reminded of several longstanding issues. I asked Claude to turn them into reports.


CmdStanR cannot tell what produced an existing executable. The binary reports a
handful of STAN_* flags via <exe> info and nothing else — not the user
header, not the include paths, not the remaining cpp_options. Anything else
lives only in the R object, so it is gone in the next session.

Consequences, all currently documented rather than fixed:

  • A fresh object configured with a user_header cannot tell whether the
    up-to-date executable was built with that header, a different one, or none, so
    it does not rebuild. Rebuilding on the possibility would recompile once per R
    session for exactly the users with the most expensive C++ builds.
  • The same holds for include_paths. Keep model state consistent with the executable, and stop dropping compile-time inputs #1235 makes changing them on a compiled
    object force a rebuild, but a fresh object has nothing to compare against.
  • $cpp_options() can only be hydrated for the few options the binary reports.

Writing a small record next to the executable at build time — user header,
include paths, resolved includes, cpp_options — and consulting it in the
up-to-date check would close all of these in both directions, and would remove
the need for exe_info_reflects_cpp_options() and its guesswork entirely. It
would also give #1019 and the issue above a reliable basis to build on.

Needs thought about where the record lives (beside the executable, so it travels
with it), what happens when it is missing (fall back to today's behaviour), and
staleness of the record itself.


Edit: An independent review of #1235 arrived at this design from a different
direction. Looking at the state model rather than at correctness, it recommended
consolidating CmdStanModel's state to two primary categories — desired/source
configuration, and a known description of the installed artifact — on the
grounds that the other two categories introduced there are derivable rather than
independent:

  • divergence (the user_header_dirty_ and include_paths_dirty_ latches) is
    derivable by comparing the desired inputs against the inputs committed for the
    artifact;
  • built_cpp_options_ is artifact provenance — evidence about the artifact —
    rather than a state category of its own.

The proposed shape is a committed artifact/build snapshot holding the built
header, ordered include paths, explicit make options, reported options, and
generated-code ownership, with an explicit unknown value covering adopted
executables. That would eliminate both dirty latches and the load-bearing
length(include_paths_) > 0 proxy, while preserving the policy that unknown
provenance does not automatically trigger a rebuild.

That is the same record this issue proposes, viewed as an in-memory state model
rather than a file beside the executable — so the two are one design at
different levels of persistence, and implementing the record here would
naturally subsume the consolidation. Worth noting mainly because two reviewers
reached it independently, one from correctness and one from architecture.

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