1# Copyright (c) 2021 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14cmake_minimum_required(VERSION 3.5.2 FATAL_ERROR) 15 16# Generate files based on templates and YAML data provided. 17# Adds targets for every template. Also adds a target for the whole function invocation 18# with name ${data_name}_gen_${PROJECT_NAME} for ease of declaring dependencies on generated files. 19# 20# Mandatory arguments: 21# * DATA -- data source, YAML file 22# * TEMPLATES -- a list of templates to generate files 23# * REQUIRES -- a list of Ruby scripts that provide data-querying API for templates 24# 25# Optional arguments: 26# * SOURCE -- a directory with templates, default is ${PROJECT_SOURCE_DIR}/templates 27# * DESTINATION -- a directory for output files, default is ${PANDA_BINARY_ROOT} 28# * EXTRA_DEPENDENCIES -- a list of files that should be considered as dependencies 29 30function(panda_gen) 31 set(singlevalues DATA SOURCE DESTINATION TARGET_NAME) 32 set(multivalues TEMPLATES REQUIRES EXTRA_DEPENDENCIES) 33 cmake_parse_arguments( 34 GEN_ARG 35 "" 36 "${singlevalues}" 37 "${multivalues}" 38 ${ARGN} 39 ) 40 41 if (NOT DEFINED GEN_ARG_TEMPLATES) 42 message(FATAL_ERROR "`TEMPLATES` were not passed to `panda_gen` function") 43 endif() 44 45 if (NOT DEFINED GEN_ARG_DATA) 46 message(FATAL_ERROR "`DATA` was not passed to `panda_gen` function") 47 endif() 48 49 if (NOT DEFINED GEN_ARG_SOURCE) 50 set(GEN_ARG_SOURCE "${PROJECT_SOURCE_DIR}/templates") 51 endif() 52 53 if (NOT DEFINED GEN_ARG_DESTINATION) 54 set(GEN_ARG_DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") 55 endif() 56 57 if (NOT DEFINED GEN_ARG_TARGET_NAME) 58 get_filename_component(DATA_NAME ${GEN_ARG_DATA} NAME_WE) 59 set(GEN_ARG_TARGET_NAME "${DATA_NAME}_gen_${PROJECT_NAME}") 60 endif() 61 62 add_custom_target(${GEN_ARG_TARGET_NAME}) # Umbrella target for all generated files 63 64 foreach(t ${GEN_ARG_TEMPLATES}) 65 set(TEMPLATE "${GEN_ARG_SOURCE}/${t}") 66 string(REGEX REPLACE "\.erb$" "" NAME ${t}) 67 string(REPLACE "\." "_" TARGET ${NAME}) 68 string(REPLACE "/" "_" TARGET ${TARGET}) 69 set(TARGET ${PROJECT_NAME}_${TARGET}) 70 set(OUTPUT_FILE "${GEN_ARG_DESTINATION}/${NAME}") 71 72 panda_gen_file(DATAFILE ${GEN_ARG_DATA} 73 TEMPLATE ${TEMPLATE} 74 OUTPUTFILE ${OUTPUT_FILE} 75 REQUIRES ${GEN_ARG_REQUIRES} 76 EXTRA_DEPENDENCIES ${GEN_ARG_EXTRA_DEPENDENCIES} 77 ) 78 add_custom_target(${TARGET} DEPENDS ${OUTPUT_FILE}) 79 add_dependencies(${GEN_ARG_TARGET_NAME} ${TARGET}) 80 endforeach() 81endfunction() 82 83# Calls `panda_gen` for ISA YAML. 84# Adds targets for every template. Also adds a target for the whole function invocation 85# with name isa_gen_${PROJECT_NAME} for ease of declaring dependencies on generated files. 86# 87# Mandatory arguments: 88# * TEMPLATES -- a list of templates to generate files 89# 90# Optional arguments: 91# * SOURCE -- a directory with templates, default is ${PROJECT_SOURCE_DIR}/templates 92# * DESTINATION -- a directory for output files, default is ${PANDA_BINARY_ROOT} 93# * REQUIRES -- if defined, will require additional Ruby files for template generation 94# * EXTRA_DEPENDENCIES -- a list of files that should be considered as dependencies 95 96function(panda_isa_gen) 97 set(singlevalues SOURCE DESTINATION) 98 set(multivalues TEMPLATES REQUIRES EXTRA_DEPENDENCIES) 99 cmake_parse_arguments( 100 ISA_GEN_ARG 101 "" 102 "${singlevalues}" 103 "${multivalues}" 104 ${ARGN} 105 ) 106 set(ISA_DATA "${CMAKE_BINARY_DIR}/isa/isa.yaml") 107 set(ISAPI "${PANDA_ROOT}/isa/isapi.rb") 108 list(INSERT ISA_GEN_ARG_REQUIRES 0 ${ISAPI}) 109 list(APPEND ISA_GEN_ARG_EXTRA_DEPENDENCIES isa_assert) 110 panda_gen(DATA ${ISA_DATA} 111 TEMPLATES ${ISA_GEN_ARG_TEMPLATES} 112 SOURCE ${ISA_GEN_ARG_SOURCE} 113 DESTINATION ${ISA_GEN_ARG_DESTINATION} 114 REQUIRES ${ISA_GEN_ARG_REQUIRES} 115 EXTRA_DEPENDENCIES ${ISA_GEN_ARG_EXTRA_DEPENDENCIES} 116 ) 117endfunction() 118 119# Generate file for a template and YAML data provided. 120# 121# Mandatory arguments: 122# DATAFILE -- YAML data full name 123# TEMPLATE -- template full name 124# OUTPUTFILE -- output file full name 125# REQUIRES -- a list of scripts that provide data-querying API for templates 126# EXTRA_DEPENDENCIES -- a list of files that should be considered as dependencies 127 128function(panda_gen_file) 129 set(singlevalues DATAFILE TEMPLATE OUTPUTFILE) 130 set(multivalues REQUIRES EXTRA_DEPENDENCIES) 131 cmake_parse_arguments( 132 ARG 133 "" 134 "${singlevalues}" 135 "${multivalues}" 136 ${ARGN} 137 ) 138 set(GENERATOR "${PANDA_ROOT}/isa/gen.rb") 139 string(REPLACE ";" "," REQUIRE_STR "${ARG_REQUIRES}") 140 set(DEPENDS_LIST ${GENERATOR} ${ARG_TEMPLATE} ${ARG_DATAFILE}) 141 142 foreach(r ${ARG_REQUIRES}) 143 list(APPEND DEPENDS_LIST ${r}) 144 endforeach() 145 146 foreach(r ${ARG_EXTRA_DEPENDENCIES}) 147 list(APPEND DEPENDS_LIST ${r}) 148 endforeach() 149 150 add_custom_command(OUTPUT ${ARG_OUTPUTFILE} 151 COMMENT "Generate file for ${ARG_TEMPLATE}" 152 COMMAND ${GENERATOR} --template ${ARG_TEMPLATE} --data ${ARG_DATAFILE} --output ${ARG_OUTPUTFILE} --require ${REQUIRE_STR} 153 DEPENDS ${DEPENDS_LIST} 154 ) 155endfunction() 156 157# Create an options header using a YAML file for the target 158# 159# Mandatory arguments: 160# TARGET -- target 161# YAML_FILE -- YAML file 162# GENERATED_HEADER -- generated header 163# 164# Use "#include 'generated/GENERATED_HEADER"' to include the generated header 165 166function(panda_gen_options) 167 # Parsing function arguments 168 set(singlevalues TARGET YAML_FILE GENERATED_HEADER) 169 cmake_parse_arguments(GEN_OPTIONS "" "${singlevalues}" "" ${ARGN}) 170 171 # Generate an options header 172 get_filename_component(YAML_FILE ${GEN_OPTIONS_YAML_FILE} ABSOLUTE) 173 set(GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/panda_gen_options/generated) 174 file(MAKE_DIRECTORY ${GENERATED_DIR}) 175 set(OPTIONS_H ${GENERATED_DIR}/${GEN_OPTIONS_GENERATED_HEADER}) 176 panda_gen_file( 177 DATAFILE ${YAML_FILE} 178 TEMPLATE ${PANDA_ROOT}/templates/options/options.h.erb 179 OUTPUTFILE ${OPTIONS_H} 180 REQUIRES ${PANDA_ROOT}/templates/common.rb 181 ) 182 183 # Add dependencies for a target 184 target_include_directories(${GEN_OPTIONS_TARGET} PUBLIC ${GENERATED_DIR}/..) 185 add_custom_target(${GEN_OPTIONS_TARGET}_options DEPENDS ${OPTIONS_H}) 186 add_dependencies(${GEN_OPTIONS_TARGET} ${GEN_OPTIONS_TARGET}_options) 187endfunction() 188 189function(panda_gen_messages) 190 set(singlevalues TARGET YAML_FILE GENERATED_HEADER) 191 cmake_parse_arguments(ARG "" "${singlevalues}" "" ${ARGN}) 192 193 if(NOT DEFINED ARG_YAML_FILE) 194 set(ARG_YAML_FILE ${CMAKE_CURRENT_SOURCE_DIR}/messages.yaml) 195 endif() 196 197 if(NOT DEFINED ARG_GENERATED_HEADER) 198 set(ARG_GENERATED_HEADER messages.h) 199 endif() 200 201 get_filename_component(YAML_FILE ${ARG_YAML_FILE} ABSOLUTE) 202 203 if(IS_ABSOLUTE ${ARG_GENERATED_HEADER}) 204 get_filename_component(GENERATED_DIR ${ARG_GENERATED_HEADER} DIRECTORY) 205 set(MESSAGES_H ${ARG_GENERATED_HEADER}) 206 else() 207 set(INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/panda_gen_messages) 208 set(GENERATED_DIR ${INCLUDE_DIR}/generated) 209 set(MESSAGES_H ${GENERATED_DIR}/${ARG_GENERATED_HEADER}) 210 endif() 211 212 file(MAKE_DIRECTORY ${GENERATED_DIR}) 213 panda_gen_file( 214 DATAFILE ${YAML_FILE} 215 TEMPLATE ${PANDA_ROOT}/templates/messages/messages.h.erb 216 OUTPUTFILE ${MESSAGES_H} 217 REQUIRES ${PANDA_ROOT}/templates/messages.rb 218 ) 219 220 # Add dependencies for a target 221 if (NOT DEFINED ARG_TARGET) 222 set(ARG_TARGET messages_gen_${PROJECT_NAME}) 223 add_custom_target(${ARG_TARGET}) 224 endif() 225 226 if (DEFINED INCLUDE_DIR) 227 target_include_directories(${ARG_TARGET} PUBLIC ${INCLUDE_DIR}) 228 endif() 229 add_custom_target(${ARG_TARGET}_messages DEPENDS ${MESSAGES_H}) 230 add_dependencies(${ARG_TARGET} ${ARG_TARGET}_messages) 231endfunction() 232