blob: 39c405f73583b79f1ddfa6455d99bbb71e1dea56 [file] [log] [blame] [edit]
# This CMake include file defines utility functions for handling generated files
# that are required in another CMake directory other than the one where it was
# generated.
#
# Key functions:
#
# * ADD_FILE_TARGET - Creates a file target. All source files, whether
# generated or not should call this function.
# * GET_FILE_TARGET - Given a absolute or local source path, what is the target
# that will build this file.
# * GET_REL_TARGET - Given a absolute or local source path and prefix
# generates target name in a form $prefix_$file.
# * GET_FILE_LOCATION - Given a absolute or local source path, what is the
# actual location of the file.
#
# When writing targets that rely on files using these functions, never use a raw
# source path, e.g. ${CMAKE_CURRENT_SOURCE_DIR}/example_dir/example.file or
# example.file. Instead always call GET_FILE_LOCATION on the path. This is
# because the actual source location may not be in the source tree, but instead
# be in the build tree.
#
# When writing targets that rely on files using these functions, always DEPEND
# on both the file location (from GET_FILE_LOCATION) and the file target (from
# GET_FILE_TARGET). If you only DEPEND on the file location, the file will not
# be generated the first time through the build.
#
# Utility functions:
#
# * APPEND_FILE_LOCATION - Appends to a list the file location of specified
# file.
# * APPEND_FILE_DEPENDENCY - Appends to a list both the file location and file
# target of specified file.
function(GET_REL_TARGET var prefix src_file)
if(${src_file} MATCHES "^/")
set(SOURCE_LOCATION ${src_file})
else()
set(SOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/${src_file})
endif()
get_filename_component(CANON_LOCATION ${SOURCE_LOCATION}
ABSOLUTE
BASE_DIR ${f4pga-arch-defs_SOURCE_DIR}
)
string(
REPLACE
"${f4pga-arch-defs_SOURCE_DIR}"
""
REL_CANON_LOCATION
${CANON_LOCATION}
)
string(
REPLACE
"/"
"_"
TARGET_PATH
${REL_CANON_LOCATION}
)
set(${var} ${prefix}${TARGET_PATH} PARENT_SCOPE)
endfunction()
function(GET_FILE_TARGET var src_file)
get_rel_target(TARGET_PATH file ${src_file})
set(${var} ${TARGET_PATH} PARENT_SCOPE)
endfunction()
function(GET_FILE_LOCATION var src_file)
# Sets var in PARENT_SCOPE to file location for given src_file.
get_file_target(SRC_TARGET ${src_file})
get_target_property(SRC_LOCATION ${SRC_TARGET} LOCATION)
if("${SRC_LOCATION}" STREQUAL "NOT_FOUND")
message(
FATAL_ERROR
"File ${src_file} is not a valid verilog target, missing LOCATION."
)
endif()
set(${var} ${SRC_LOCATION} PARENT_SCOPE)
endfunction()
function(APPEND_FILE_LOCATION var src_file)
# Appends to list var in PARENT_SCOPE both file location for
# given src_file.
get_file_target(SRC_TARGET ${src_file})
get_target_property(SRC_LOCATION ${SRC_TARGET} LOCATION)
if("${SRC_LOCATION}" STREQUAL "NOT_FOUND")
message(
FATAL_ERROR
"File ${src_file} is not a valid verilog target, missing LOCATION."
)
endif()
list(APPEND ${var} ${SRC_LOCATION})
set(${var} "${${var}}" PARENT_SCOPE)
endfunction()
function(APPEND_FILE_DEPENDENCY var src_file)
# Appends to list var in PARENT_SCOPE both file location and file target for
# given src_file.
get_file_target(SRC_TARGET ${src_file})
get_target_property(SRC_LOCATION ${SRC_TARGET} LOCATION)
if("${SRC_LOCATION}" STREQUAL "NOT_FOUND")
message(
FATAL_ERROR
"File ${src_file} is not a valid verilog target, missing LOCATION."
)
endif()
list(APPEND ${var} ${SRC_TARGET})
list(APPEND ${var} ${SRC_LOCATION})
get_target_property(INCLUDE_FILES ${SRC_TARGET} INCLUDE_FILES)
foreach(SRC ${INCLUDE_FILES})
append_file_dependency(${var} ${SRC})
endforeach()
set(${var} "${${var}}" PARENT_SCOPE)
endfunction()
function(APPEND_FILE_INCLUDES var src_file)
# Appends to list var in PARENT_SCOPE both includes listed for file target for
# given src_file.
get_file_target(SRC_TARGET ${src_file})
get_target_property(SRC_INCLUDES ${SRC_TARGET} INCLUDES)
if("${SRC_INCLUDES}" STREQUAL "NOT_FOUND")
message(
FATAL_ERROR
"File ${SRC} is not a valid verilog target, missing INCLUDES list."
)
endif()
list(APPEND ${var} ${SRC_INCLUDES})
set(${var} "${${var}}" PARENT_SCOPE)
endfunction()
function(GET_VERILOG_INCLUDES var file)
# Appends to list var in PARENT_SCOPE all verilog source dependency based on
# scanning input at configure time.
#
# Note this function cannot be used at generation time because execute_process
# is called during configure step and generated files don't exist yet.
execute_process(
COMMAND
${PYTHON_EXECUTABLE} ${f4pga-arch-defs_SOURCE_DIR}/utils/deps_verilog.py
--file_per_line ${CMAKE_CURRENT_SOURCE_DIR}/${file}
WORKING_DIRECTORY ${f4pga-arch-defs_SOURCE_DIR}
OUTPUT_VARIABLE INCLUDES
)
string(
REPLACE
"\n"
";"
INCLUDES_LIST
"${INCLUDES}"
)
foreach(INCLUDE ${INCLUDES_LIST})
string(STRIP ${INCLUDE} INCLUDE)
if(NOT "${INCLUDE}" STREQUAL "")
get_filename_component(
ABS_PATH_TO_INCLUDE
${INCLUDE}
ABSOLUTE
BASE_DIR
${f4pga-arch-defs_SOURCE_DIR}
)
list(APPEND ${var} ${ABS_PATH_TO_INCLUDE})
endif()
endforeach()
set(${var} "${${var}}" PARENT_SCOPE)
endfunction()
function(GET_XML_INCLUDES var file)
# Appends to list var in PARENT_SCOPE all xml source dependency based on
# scanning input at configure time.
#
# Note this function cannot be used at generation time because execute_process
# is called during configure step and generated files don't exist yet.
execute_process(
COMMAND
${PYTHON_EXECUTABLE} ${f4pga-arch-defs_SOURCE_DIR}/utils/deps_xml.py
--file_per_line ${CMAKE_CURRENT_SOURCE_DIR}/${file}
WORKING_DIRECTORY ${f4pga-arch-defs_SOURCE_DIR}
OUTPUT_VARIABLE INCLUDES
)
string(
REPLACE
"\n"
";"
INCLUDES_LIST
"${INCLUDES}"
)
foreach(INCLUDE ${INCLUDES_LIST})
string(STRIP ${INCLUDE} INCLUDE)
if(NOT "${INCLUDE}" STREQUAL "")
get_filename_component(
ABS_PATH_TO_INCLUDE
${INCLUDE}
ABSOLUTE
BASE_DIR
${f4pga-arch-defs_SOURCE_DIR}
)
list(APPEND ${var} ${ABS_PATH_TO_INCLUDE})
endif()
endforeach()
set(${var} "${${var}}" PARENT_SCOPE)
endfunction()
function(ADD_FILE_TARGET)
# ~~~
# ADD_FILE_TARGET(
# FILE <source file location>
# [GENERATED | SCANNER_TYPE <verilog|xml>]
# [ABSOLUTE]
# )
# ~~~
#
# Creates new file target for given source location. Even if ADD_FILE_TARGET
# is being called on a file that is located in the binary directory, always
# pass the source location it would have if the source and binary directories
# were the same. This is important for dependency definitions that assume all
# inputs are in one folder.
#
# ADD_FILE_TARGET ensures that all sources are in one folder, because of how
# the verilog include directive is defined.
#
# SCANNER_TYPE argument can be used if GENERATED or ABSOLUTE are not set. Valid
# SCANNER_TYPE are "verilog" and "xml".
# ABSOLUTE parameter must be used to mark passed path is absolute
#
# GENERATED must be passed if the source file is generated and is placed
# within the binary directory.
set(options GENERATED ABSOLUTE)
set(oneValueArgs FILE SCANNER_TYPE)
set(multiValueArgs)
cmake_parse_arguments(
ADD_FILE_TARGET
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
get_file_target(TARGET_NAME ${ADD_FILE_TARGET_FILE})
set(INCLUDE_FILES "")
if(NOT ${ADD_FILE_TARGET_GENERATED})
if("${ADD_FILE_TARGET_SCANNER_TYPE}" STREQUAL "")
elseif("${ADD_FILE_TARGET_SCANNER_TYPE}" STREQUAL "verilog")
get_verilog_includes(INCLUDE_FILES ${ADD_FILE_TARGET_FILE})
elseif("${ADD_FILE_TARGET_SCANNER_TYPE}" STREQUAL "xml")
get_xml_includes(INCLUDE_FILES ${ADD_FILE_TARGET_FILE})
else()
message(
FATAL_ERROR "Unknown SCANNER_TYPE=${ADD_FILE_TARGET_SCANNER_TYPE}."
)
endif()
endif()
set(INCLUDE_FILES_TARGETS "")
foreach(INCLUDE ${INCLUDE_FILES})
append_file_dependency(INCLUDE_FILES_TARGETS ${INCLUDE})
endforeach()
if(NOT ${ADD_FILE_TARGET_GENERATED} AND NOT ${ADD_FILE_TARGET_ABSOLUTE})
get_filename_component(DEST_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ADD_FILE_TARGET_FILE} DIRECTORY)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ADD_FILE_TARGET_FILE}
COMMAND
${CMAKE_COMMAND} -E make_directory
${DEST_PATH}
COMMAND
${CMAKE_COMMAND} -E create_symlink
${CMAKE_CURRENT_SOURCE_DIR}/${ADD_FILE_TARGET_FILE}
${CMAKE_CURRENT_BINARY_DIR}/${ADD_FILE_TARGET_FILE}
)
endif()
if(${ADD_FILE_TARGET_ABSOLUTE})
set(FILE_PATH ${ADD_FILE_TARGET_FILE})
else()
set(FILE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ADD_FILE_TARGET_FILE})
endif()
add_custom_target(
${TARGET_NAME}
DEPENDS
${FILE_PATH}
${INCLUDE_FILES_TARGETS}
)
set_target_properties(
${TARGET_NAME}
PROPERTIES LOCATION ${FILE_PATH}
)
set_target_properties(${TARGET_NAME} PROPERTIES INCLUDE_FILES "${INCLUDE_FILES}")
set_target_properties(${TARGET_NAME} PROPERTIES INCLUDES "")
set_target_properties(${TARGET_NAME} PROPERTIES INCLUDE_FILES "${INCLUDE_FILES}")
endfunction(ADD_FILE_TARGET)