mirror of
https://github.com/fluffos/fluffos
synced 2026-08-12 18:26:06 -04:00
Every local build regenerated grammar.autogen.cc/.h and lexer.autogen.cc
and copied them back into the source tree, churning git whenever the
host's bison/flex emitted cosmetically different output than whatever
produced the committed copy. The existing normalization only tokenized
paths and header guards; it could not help when two generators that even
REPORT the same version (3.8.2) differ in emitted code (the committed
grammar had a distro-patched bison's "int yynerrs YY_ATTRIBUTE_UNUSED;"
that vanilla 3.8.2 downgrades on every rebuild).
Two-layer fix:
- Version pin: the committed generated files record the generator that
produced them ("made by GNU Bison X.Y.Z", YY_FLEX_*_VERSION). A host
with an OLDER generator no longer regenerates at all -- it builds from
the committed copy via the existing pre-generated path. Hosts at/above
the pin still regenerate, so CI keeps validating grammar.y/lexer.l
against a real toolchain.
- Input-hash gate: the post-build copy-back stamps the committed file
with the sha256 of its INPUT (grammar.y / lexer.l) and is skipped
entirely while that hash is unchanged. Generator differences alone can
no longer touch the committed files; editing the .y/.l still updates
them exactly as before.
Also: the copy-back now skips writing byte-identical content, so it no
longer bumps mtimes and cascades pointless relinks on every build.
The three committed autogen files are re-stamped in this commit (one-time
churn) as the new pinned baseline: bison 3.8.2 / flex 2.6.4, matching
every CI image.
Claude-Session: https://claude.ai/code/session_01PCUi8QKaFxeTyLXMeVkqn4
Co-authored-by: Claude <noreply@anthropic.com>
104 lines
4.3 KiB
CMake
104 lines
4.3 KiB
CMake
function(process_bison_file FILE_IN FILE_OUT REPO_ROOT ACTION BUILD_ROOT SRC_FILE)
|
|
if (NOT EXISTS "${FILE_IN}")
|
|
message(WARNING "File does not exist: ${FILE_IN}")
|
|
return()
|
|
endif()
|
|
|
|
# Resolve REPO_ROOT to an absolute path first
|
|
get_filename_component(REPO_ROOT_ABS "${REPO_ROOT}" ABSOLUTE)
|
|
string(REPLACE "\\" "/" REPO_ROOT_NORM "${REPO_ROOT_ABS}")
|
|
if (REPO_ROOT_NORM MATCHES "/$")
|
|
string(REGEX REPLACE "/$" "" REPO_ROOT_NORM "${REPO_ROOT_NORM}")
|
|
endif()
|
|
|
|
# Resolve the build root too: it must be tokenized separately (and
|
|
# first, since it usually nests inside the repo root) so the committed
|
|
# file does not depend on the build directory's name or location.
|
|
set(BUILD_ROOT_NORM "")
|
|
if (BUILD_ROOT)
|
|
get_filename_component(BUILD_ROOT_ABS "${BUILD_ROOT}" ABSOLUTE)
|
|
string(REPLACE "\\" "/" BUILD_ROOT_NORM "${BUILD_ROOT_ABS}")
|
|
if (BUILD_ROOT_NORM MATCHES "/$")
|
|
string(REGEX REPLACE "/$" "" BUILD_ROOT_NORM "${BUILD_ROOT_NORM}")
|
|
endif()
|
|
endif()
|
|
|
|
file(READ "${FILE_IN}" CONTENT)
|
|
|
|
if (ACTION STREQUAL "NORMALIZE")
|
|
# When the generator input (.y/.l) is known, gate the copy-back on it:
|
|
# if the committed file already records the same input hash, the
|
|
# committed file is up to date with respect to the grammar/lexer
|
|
# SOURCE, and regeneration differences (bison/flex build variations
|
|
# between hosts that report the same version) must NOT churn it.
|
|
if (SRC_FILE AND EXISTS "${SRC_FILE}" AND EXISTS "${FILE_OUT}")
|
|
file(SHA256 "${SRC_FILE}" SRC_HASH)
|
|
file(READ "${FILE_OUT}" EXISTING_OUT)
|
|
string(REGEX MATCH "FluffOS generated-from [^ ]+ sha256=([0-9a-f]+)" _stamp "${EXISTING_OUT}")
|
|
if ("${CMAKE_MATCH_1}" STREQUAL "${SRC_HASH}")
|
|
return()
|
|
endif()
|
|
endif()
|
|
|
|
# Replace absolute paths with standard tokens (build root first).
|
|
set(CONTENT_SUB "${CONTENT}")
|
|
if (BUILD_ROOT_NORM)
|
|
string(REPLACE "${BUILD_ROOT_NORM}" "$BUILD_ROOT$" CONTENT_SUB "${CONTENT_SUB}")
|
|
endif()
|
|
string(REPLACE "${REPO_ROOT_NORM}" "$REPO_ROOT$" CONTENT_SUB "${CONTENT_SUB}")
|
|
# Bison mangles the build-dir path into the header guard (uppercased, slashes→underscores).
|
|
# Replace it with a fixed token so the committed file is platform-independent.
|
|
string(REGEX REPLACE
|
|
"YY_YY_[A-Z0-9_]+_GRAMMAR_AUTOGEN_H_INCLUDED"
|
|
"YY_YY_GRAMMAR_AUTOGEN_H_INCLUDED"
|
|
CONTENT_SUB "${CONTENT_SUB}")
|
|
# Record which input produced this file so later builds can tell "the
|
|
# grammar/lexer changed" apart from "a different generator build
|
|
# produced cosmetically different output".
|
|
if (SRC_FILE AND EXISTS "${SRC_FILE}")
|
|
file(SHA256 "${SRC_FILE}" SRC_HASH)
|
|
get_filename_component(SRC_NAME "${SRC_FILE}" NAME)
|
|
string(APPEND CONTENT_SUB "/* FluffOS generated-from ${SRC_NAME} sha256=${SRC_HASH} */\n")
|
|
endif()
|
|
elseif (ACTION STREQUAL "DENORMALIZE")
|
|
# Replace standard tokens with the actual absolute local paths
|
|
set(CONTENT_SUB "${CONTENT}")
|
|
if (BUILD_ROOT_NORM)
|
|
string(REPLACE "$BUILD_ROOT$" "${BUILD_ROOT_NORM}" CONTENT_SUB "${CONTENT_SUB}")
|
|
endif()
|
|
string(REPLACE "$REPO_ROOT$" "${REPO_ROOT_NORM}" CONTENT_SUB "${CONTENT_SUB}")
|
|
else()
|
|
message(FATAL_ERROR "Invalid ACTION: ${ACTION}. Must be NORMALIZE or DENORMALIZE")
|
|
endif()
|
|
|
|
# Skip the write when the destination already has identical content --
|
|
# rewriting identical bytes still bumps the mtime and cascades a
|
|
# pointless rebuild/relink of everything downstream on the next build.
|
|
if (EXISTS "${FILE_OUT}")
|
|
file(READ "${FILE_OUT}" EXISTING_CONTENT)
|
|
if ("${EXISTING_CONTENT}" STREQUAL "${CONTENT_SUB}")
|
|
return()
|
|
endif()
|
|
endif()
|
|
|
|
file(WRITE "${FILE_OUT}" "${CONTENT_SUB}")
|
|
if (ACTION STREQUAL "NORMALIZE")
|
|
message(STATUS "Normalized and copied ${FILE_IN} -> ${FILE_OUT}")
|
|
else()
|
|
message(STATUS "Denormalized and copied ${FILE_IN} -> ${FILE_OUT}")
|
|
endif()
|
|
endfunction()
|
|
|
|
if (CMAKE_SCRIPT_MODE_FILE)
|
|
if (NOT DEFINED ACTION OR NOT DEFINED FILE_IN OR NOT DEFINED FILE_OUT OR NOT DEFINED REPO_ROOT)
|
|
message(FATAL_ERROR "Required variables: ACTION, FILE_IN, FILE_OUT, REPO_ROOT")
|
|
endif()
|
|
if (NOT DEFINED BUILD_ROOT)
|
|
set(BUILD_ROOT "")
|
|
endif()
|
|
if (NOT DEFINED SRC_FILE)
|
|
set(SRC_FILE "")
|
|
endif()
|
|
|
|
process_bison_file("${FILE_IN}" "${FILE_OUT}" "${REPO_ROOT}" "${ACTION}" "${BUILD_ROOT}" "${SRC_FILE}")
|
|
endif()
|