| # Copyright 2026 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| include($ENV{PW_ROOT}/pw_build/pigweed.cmake) |
| |
| # Generates a C++ library with versioned enums. |
| # |
| # Args: |
| # HEADERS: Standard .h files containing enum definitions. |
| # PUBLIC_DEPS: Dependencies (other pw_cc_enum targets). |
| # PUBLIC_INCLUDES: Prefix to strip from the include path of generated headers. |
| function(pw_cc_enum NAME) |
| pw_parse_arguments( |
| NUM_POSITIONAL_ARGS 1 |
| ONE_VALUE_ARGS PUBLIC_INCLUDES |
| MULTI_VALUE_ARGS HEADERS PUBLIC_DEPS |
| ) |
| |
| if(NOT arg_HEADERS) |
| message(FATAL_ERROR "pw_cc_enum requires HEADERS") |
| endif() |
| |
| list(LENGTH arg_PUBLIC_INCLUDES public_includes_len) |
| if(NOT public_includes_len EQUAL 1) |
| message(FATAL_ERROR "pw_cc_enum requires exactly one value for PUBLIC_INCLUDES") |
| endif() |
| |
| if("${arg_PUBLIC_INCLUDES}" STREQUAL ".") |
| message(FATAL_ERROR "pw_cc_enum does not allow '.' as PUBLIC_INCLUDES. " |
| "Since CMake doesn't isolate headers, it is too easy for sources to " |
| "accidentally include original headers instead of generated ones.") |
| endif() |
| |
| set(out_dir "${CMAKE_CURRENT_BINARY_DIR}/${NAME}") |
| |
| # Write the placeholder C++ source file. |
| file(WRITE "${out_dir}/base.cc" "// placeholder\n") |
| |
| # Create an internal target to collect includes and options from dependencies. |
| add_library("${NAME}._base_lib" OBJECT "${out_dir}/base.cc") |
| target_include_directories("${NAME}._base_lib" PRIVATE |
| "${arg_PUBLIC_INCLUDES}" |
| ) |
| target_link_libraries("${NAME}._base_lib" PRIVATE |
| pw_enum._generate_internal_do_not_use |
| ${arg_PUBLIC_DEPS} |
| ) |
| |
| # Determine the C++ standard to use. |
| get_target_property(target_cxx_std "${NAME}._base_lib" CXX_STANDARD) |
| if(NOT target_cxx_std) |
| set(target_cxx_std "${CMAKE_CXX_STANDARD}") |
| endif() |
| if(NOT target_cxx_std) |
| set(target_cxx_std "17") |
| endif() |
| |
| # Collect compilation flags. |
| set(content "") |
| string(REPLACE " " "\n" cxx_flags "${CMAKE_CXX_FLAGS}") |
| set(content "${cxx_flags}\n") |
| set(content "${content}-std=c++${target_cxx_std}\n") |
| set(content "${content}$<IF:$<BOOL:$<TARGET_PROPERTY:${NAME}._base_lib,INCLUDE_DIRECTORIES>>,-I$<JOIN:$<TARGET_PROPERTY:${NAME}._base_lib,INCLUDE_DIRECTORIES>,\n-I>\n,>") |
| set(content "${content}$<IF:$<BOOL:$<TARGET_PROPERTY:${NAME}._base_lib,COMPILE_DEFINITIONS>>,-D$<JOIN:$<TARGET_PROPERTY:${NAME}._base_lib,COMPILE_DEFINITIONS>,\n-D>\n,>") |
| set(content "${content}${out_dir}/base.cc\n") |
| |
| file(GENERATE |
| OUTPUT "${out_dir}/flags.txt" |
| CONTENT "${content}" |
| ) |
| |
| set(output_files "") |
| foreach(header IN LISTS arg_HEADERS) |
| cmake_path(IS_ABSOLUTE header is_absolute) |
| if(is_absolute) |
| message(FATAL_ERROR |
| "pw_cc_enum HEADERS must be relative, but '${header}' is absolute") |
| endif() |
| |
| cmake_path(IS_PREFIX arg_PUBLIC_INCLUDES "${header}" NORMALIZE is_prefix) |
| if(NOT is_prefix) |
| message(FATAL_ERROR |
| "pw_cc_enum HEADERS must be nested under the PUBLIC_INCLUDES path, " |
| "but '${header}' is not nested under '${arg_PUBLIC_INCLUDES}'.") |
| endif() |
| |
| cmake_path(RELATIVE_PATH header BASE_DIRECTORY "${arg_PUBLIC_INCLUDES}" |
| OUTPUT_VARIABLE relative_header) |
| list(APPEND output_files "${out_dir}/include/${relative_header}") |
| endforeach() |
| |
| add_custom_command( |
| OUTPUT ${output_files} |
| COMMAND python3 $ENV{PW_ROOT}/pw_enum/py/pw_enum/generate.py |
| ${arg_HEADERS} |
| "--outputs" ${output_files} |
| "--compiler" "${CMAKE_CXX_COMPILER}" |
| "--compiler-flags" "${out_dir}/flags.txt" |
| "--base-cc" "${out_dir}/base.cc" |
| DEPENDS ${arg_HEADERS} |
| "${out_dir}/flags.txt" |
| "$ENV{PW_ROOT}/pw_enum/py/pw_enum/generate.py" |
| WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" |
| COMMENT "Generating versioned enums for ${NAME}" |
| ) |
| |
| add_custom_target("${NAME}.generate" DEPENDS ${output_files}) |
| |
| pw_add_library("${NAME}" INTERFACE |
| PUBLIC_DEPS |
| pw_tokenizer |
| pw_enum._generate_internal_do_not_use |
| ${arg_PUBLIC_DEPS} |
| PUBLIC_COMPILE_OPTIONS |
| # Use -iquote so generated headers take priority over source directories. |
| "-iquote${out_dir}/include" |
| ) |
| add_dependencies("${NAME}" "${NAME}.generate") |
| endfunction() |