From fae4b1241b1a60fbea6ff6e2dc037b973dcdbd67 Mon Sep 17 00:00:00 2001 From: ExPikaPaka Date: Tue, 28 Jul 2026 10:17:59 +0200 Subject: [PATCH] fix(codegen): don't run build-time codegen with the embed Python (fixes CI build) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- cmake/modules/ConfigCodegen.cmake | 83 +++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/cmake/modules/ConfigCodegen.cmake b/cmake/modules/ConfigCodegen.cmake index f8d5e3a7a2..681c300f64 100644 --- a/cmake/modules/ConfigCodegen.cmake +++ b/cmake/modules/ConfigCodegen.cmake @@ -14,24 +14,60 @@ find_program(PROTOC_EXECUTABLE protoc) 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") -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( - COMMAND ${Python3_EXECUTABLE} "${CMAKE_SOURCE_DIR}/tools/run_codegen.py" --no-validate - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE _codegen_result + COMMAND ${ORCA_CODEGEN_PYTHON} -c + "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_probe + OUTPUT_QUIET ERROR_QUIET ) - if(NOT _codegen_result EQUAL 0) - message(FATAL_ERROR "Config codegen failed. Install grpcio-tools: pip install grpcio-tools") + if(_codegen_probe EQUAL 0) + 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=.") 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() 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}/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) file(GLOB CONFIG_PROTO_FILES @@ -63,12 +100,12 @@ set(CONFIG_PROTO_FILES ${CONFIG_PROTO_FILES} ) -if(Python3_EXECUTABLE) +if(_codegen_usable) # Single command: run_codegen.py handles protoc/grpcio-tools detection internally. # Proto files → generated .cpp files. Runs automatically when any .proto changes. add_custom_command( 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} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" COMMENT "Re-generating config C++ from changed .proto files" @@ -83,17 +120,21 @@ if(Python3_EXECUTABLE) # Validation target: cmake --build . --target validate_config add_custom_target(validate_config - COMMAND ${Python3_EXECUTABLE} ${VALIDATE_TOOL} + COMMAND ${ORCA_CODEGEN_PYTHON} ${VALIDATE_TOOL} DEPENDS ${CONFIG_GENERATED_SOURCES} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Validating generated config code against PrintConfig.cpp" 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") 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()