fix(codegen): don't run build-time codegen with the embed Python (fixes CI build)

main forces Python3_EXECUTABLE to the bundled *embed* interpreter (for the
in-app Python plugin runtime). ConfigCodegen.cmake used that same interpreter to
regenerate the config sources at build time, but the embed Python has no protoc
/ protobuf / pyyaml (and for cross-compiled targets isn't even the host arch),
so ninja's "Re-generating config C++ from changed .proto files" step failed with
"protoc not found" on every platform — even though the workflow's dedicated
codegen step had already generated the files.

Probe whether the interpreter can actually run the codegen (protobuf importable
AND a protoc available, standalone or via grpc_tools). Only wire up the
auto-regenerating custom command when it can; otherwise use the already-generated
files as-is (with a no-op codegen_config target) and only error if they are
missing. Adds ORCA_CODEGEN_PYTHON to let a build point at a tools-capable
interpreter independent of the embed one.

Generated files remain gitignored; the CI "Install codegen tools and generate
config sources" step still produces them before the build.
This commit is contained in:
ExPikaPaka
2026-07-28 10:17:59 +02:00
parent 5a5c25b8d1
commit fae4b1241b

View File

@@ -14,24 +14,60 @@
find_program(PROTOC_EXECUTABLE protoc) find_program(PROTOC_EXECUTABLE protoc)
find_package(Python3 COMPONENTS Interpreter QUIET) find_package(Python3 COMPONENTS Interpreter QUIET)
# If generated files are missing (fresh clone), run codegen immediately at configure time.
# This allows cmake configure + build to work without a separate pre-build step.
set(_generated_marker "${CMAKE_SOURCE_DIR}/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp") set(_generated_marker "${CMAKE_SOURCE_DIR}/src/slic3r/GUI/generated/PrintConfigDef_generated.cpp")
if(Python3_EXECUTABLE AND NOT EXISTS "${_generated_marker}")
message(STATUS "Config codegen: generated files missing — running codegen now...") # Decide which interpreter can actually run the codegen.
#
# The main CMakeLists forces Python3_EXECUTABLE to the *bundled embed* interpreter (used for
# the in-app Python plugin runtime). That interpreter has neither protoc nor the protobuf /
# pyyaml packages, and for cross-compiled targets it may not even be the host architecture — so
# it cannot run tools/run_codegen.py. In CI the generated sources are produced by a dedicated
# pre-build "Install codegen tools and generate config sources" step; developers run
# tools/run_codegen.py themselves. Only wire up (re)generation when the interpreter really has
# the toolchain, otherwise fall back to the already-generated files.
#
# ORCA_CODEGEN_PYTHON lets a build explicitly point at an interpreter that has the tools
# (independent of the embed interpreter). Falls back to Python3_EXECUTABLE.
if(NOT ORCA_CODEGEN_PYTHON)
set(ORCA_CODEGEN_PYTHON "${Python3_EXECUTABLE}")
endif()
set(_codegen_usable FALSE)
if(ORCA_CODEGEN_PYTHON)
# Needs the protobuf python runtime (config_metadata_pb2) plus a protoc (standalone or
# grpc_tools.protoc). Probe the interpreter for both.
execute_process( execute_process(
COMMAND ${Python3_EXECUTABLE} "${CMAKE_SOURCE_DIR}/tools/run_codegen.py" --no-validate COMMAND ${ORCA_CODEGEN_PYTHON} -c
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" "import shutil, importlib.util, google.protobuf; import sys; sys.exit(0 if (shutil.which('protoc') or importlib.util.find_spec('grpc_tools')) else 1)"
RESULT_VARIABLE _codegen_result RESULT_VARIABLE _codegen_probe
OUTPUT_QUIET ERROR_QUIET
) )
if(NOT _codegen_result EQUAL 0) if(_codegen_probe EQUAL 0)
message(FATAL_ERROR "Config codegen failed. Install grpcio-tools: pip install grpcio-tools") set(_codegen_usable TRUE)
endif()
endif()
# If generated files are missing (fresh clone) and we can run codegen, do it now at configure
# time so a plain cmake configure + build works without a separate pre-build step.
if(NOT EXISTS "${_generated_marker}")
if(_codegen_usable)
message(STATUS "Config codegen: generated files missing — running codegen now...")
execute_process(
COMMAND ${ORCA_CODEGEN_PYTHON} "${CMAKE_SOURCE_DIR}/tools/run_codegen.py" --no-validate
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE _codegen_result
)
if(NOT _codegen_result EQUAL 0)
message(FATAL_ERROR "Config codegen failed. Install grpcio-tools: pip install grpcio-tools")
endif()
message(STATUS "Config codegen: generated files created successfully")
else()
message(FATAL_ERROR
"Config codegen: generated files are missing and no usable codegen toolchain was found.\n"
"Generate them first with an interpreter that has protoc + protobuf, e.g.:\n"
" pip install grpcio-tools pyyaml && python tools/run_codegen.py\n"
"or pass -DORCA_CODEGEN_PYTHON=<python-with-tools>.")
endif() endif()
message(STATUS "Config codegen: generated files created successfully")
elseif(NOT Python3_EXECUTABLE AND NOT EXISTS "${_generated_marker}")
message(FATAL_ERROR "Config codegen: generated files missing and Python3 not found.\n"
"Install Python and grpcio-tools: pip install grpcio-tools\n"
"Then run: python tools/run_codegen.py")
endif() endif()
set(CONFIG_PROTO_DIR "${CMAKE_SOURCE_DIR}/src/PrintConfigs") set(CONFIG_PROTO_DIR "${CMAKE_SOURCE_DIR}/src/PrintConfigs")
@@ -51,6 +87,7 @@ set(CONFIG_GENERATED_SOURCES
"${CONFIG_CODEGEN_DIR}/OptionKeys_generated.cpp" "${CONFIG_CODEGEN_DIR}/OptionKeys_generated.cpp"
"${CONFIG_CODEGEN_DIR}/TabLayout_generated.cpp" "${CONFIG_CODEGEN_DIR}/TabLayout_generated.cpp"
) )
set(CONFIG_GENERATED_SOURCES "${CONFIG_GENERATED_SOURCES}" CACHE INTERNAL "Generated config cpp files")
# Collect all .proto source files (flat in src/PrintConfigs/, excluding config_metadata.proto) # Collect all .proto source files (flat in src/PrintConfigs/, excluding config_metadata.proto)
file(GLOB CONFIG_PROTO_FILES file(GLOB CONFIG_PROTO_FILES
@@ -63,12 +100,12 @@ set(CONFIG_PROTO_FILES
${CONFIG_PROTO_FILES} ${CONFIG_PROTO_FILES}
) )
if(Python3_EXECUTABLE) if(_codegen_usable)
# Single command: run_codegen.py handles protoc/grpcio-tools detection internally. # Single command: run_codegen.py handles protoc/grpcio-tools detection internally.
# Proto files → generated .cpp files. Runs automatically when any .proto changes. # Proto files → generated .cpp files. Runs automatically when any .proto changes.
add_custom_command( add_custom_command(
OUTPUT ${CONFIG_GENERATED_SOURCES} OUTPUT ${CONFIG_GENERATED_SOURCES}
COMMAND ${Python3_EXECUTABLE} ${RUN_CODEGEN_TOOL} --no-validate COMMAND ${ORCA_CODEGEN_PYTHON} ${RUN_CODEGEN_TOOL} --no-validate
DEPENDS ${CONFIG_PROTO_FILES} ${CONFIG_LAYOUT_YAML} ${CODEGEN_TOOL} DEPENDS ${CONFIG_PROTO_FILES} ${CONFIG_LAYOUT_YAML} ${CODEGEN_TOOL}
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
COMMENT "Re-generating config C++ from changed .proto files" COMMENT "Re-generating config C++ from changed .proto files"
@@ -83,17 +120,21 @@ if(Python3_EXECUTABLE)
# Validation target: cmake --build . --target validate_config # Validation target: cmake --build . --target validate_config
add_custom_target(validate_config add_custom_target(validate_config
COMMAND ${Python3_EXECUTABLE} ${VALIDATE_TOOL} COMMAND ${ORCA_CODEGEN_PYTHON} ${VALIDATE_TOOL}
DEPENDS ${CONFIG_GENERATED_SOURCES} DEPENDS ${CONFIG_GENERATED_SOURCES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Validating generated config code against PrintConfig.cpp" COMMENT "Validating generated config code against PrintConfig.cpp"
VERBATIM VERBATIM
) )
# Export for use by subdirectories (libslic3r, etc.)
set(CONFIG_GENERATED_SOURCES "${CONFIG_GENERATED_SOURCES}" CACHE INTERNAL "Generated config cpp files")
message(STATUS "Config codegen: enabled — proto changes auto-regenerate on next build") message(STATUS "Config codegen: enabled — proto changes auto-regenerate on next build")
else() else()
message(STATUS "Config codegen: Python3 not found — run: pip install grpcio-tools && python tools/run_codegen.py") # No usable codegen toolchain in this interpreter (e.g. the bundled embed Python in CI).
# The generated files already exist (checked/produced above); use them as-is and provide a
# no-op codegen_config target so dependents that reference it still resolve.
add_custom_target(codegen_config ALL
COMMENT "Config codegen: using pre-generated files (no codegen toolchain in this interpreter)")
add_custom_target(validate_config
COMMENT "Config codegen: validation skipped (no codegen toolchain in this interpreter)")
message(STATUS "Config codegen: no codegen toolchain in '${ORCA_CODEGEN_PYTHON}' — using pre-generated files")
endif() endif()