mirror of
https://github.com/AxioDL/CodeGen.git
synced 2026-03-30 11:45:23 -07:00
270 lines
9.4 KiB
CMake
270 lines
9.4 KiB
CMake
cmake_minimum_required(VERSION 3.12)
|
|
|
|
function(add_codegen_targets
|
|
source_files
|
|
generated_files_var
|
|
input_root
|
|
output_root
|
|
include_directories
|
|
)
|
|
set(cache_path "${CMAKE_CURRENT_BINARY_DIR}/codegen_cache")
|
|
|
|
#
|
|
# Find our python interpreter, and set up some python related variables.
|
|
#
|
|
find_package(PythonInterp 3.6 REQUIRED)
|
|
set(venv_path "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv")
|
|
set(venv_dummy "${CMAKE_CURRENT_BINARY_DIR}/codegen_venv_dummy")
|
|
set(package_dummy "${CMAKE_CURRENT_BINARY_DIR}/codegen_package_dummy")
|
|
|
|
set(missing_requirements FALSE)
|
|
|
|
#
|
|
# Find libclang.
|
|
#
|
|
if (WIN32)
|
|
set(CMAKE_FIND_LIBRARY_SUFFIXES .dll)
|
|
endif()
|
|
if (APPLE)
|
|
execute_process(COMMAND xcodebuild -find-library clang
|
|
OUTPUT_VARIABLE CLANG_LIBRARY OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
else()
|
|
find_library(CLANG_LIBRARY NAMES clang libclang REQUIRED)
|
|
endif()
|
|
if ("${CLANG_LIBRARY}" STREQUAL "CLANG_LIBRARY-NOTFOUND")
|
|
message(SEND_ERROR "libclang not found")
|
|
set(missing_requirements TRUE)
|
|
endif()
|
|
|
|
#
|
|
# Find the codegen python package file
|
|
#
|
|
if ("${CODEGEN_PACKAGE}" STREQUAL "")
|
|
foreach (prefix ${CMAKE_PREFIX_PATH})
|
|
set(codegen_package_candidate "${prefix}/share/codegen/codegen-0.1.0.tar.gz")
|
|
if (EXISTS "${codegen_package_candidate}")
|
|
set(CODEGEN_PACKAGE "${codegen_package_candidate}")
|
|
break()
|
|
endif()
|
|
endforeach()
|
|
if ("${CODEGEN_PACKAGE}" STREQUAL "")
|
|
set(CODEGEN_PACKAGE "CODEGEN_PACKAGE-NOTFOUND" CACHE FILEPATH)
|
|
endif()
|
|
endif()
|
|
if ("${CODEGEN_PACKAGE}" STREQUAL "CODEGEN_PACKAGE-NOTFOUND")
|
|
message(SEND_ERROR "codegen package not found")
|
|
set(missing_requirements TRUE)
|
|
endif()
|
|
|
|
if (missing_requirements)
|
|
message(FATAL_ERROR "Missing requirements")
|
|
endif()
|
|
|
|
#
|
|
# Get name of python executable. We gotta add a '.exe' prefix if we're on windows, obviously.
|
|
#
|
|
if (WIN32)
|
|
set(sep ";")
|
|
set(python_executable_name "python.exe")
|
|
else()
|
|
set(sep ":")
|
|
set(python_executable_name "python")
|
|
endif()
|
|
|
|
#
|
|
# Determine the path of the virtual env python wrapper. It's different on Windows.
|
|
#
|
|
set(venv_python_executable_path "bin/${python_executable_name}")
|
|
if (NOT EXISTS "${venv_python_executable_path}" AND WIN32)
|
|
set(venv_python_executable_path "Scripts/${python_executable_name}")
|
|
endif()
|
|
|
|
#
|
|
# Setup the virtual env for the codegen tool to use if it hasn't been set up.
|
|
#
|
|
|
|
#
|
|
# TODO: Instead of just skipping over venv creation if a directory exists, we should ensure the venv is valid and
|
|
# the codegen package is up to date.
|
|
#
|
|
|
|
if (NOT EXISTS "${venv_dummy}")
|
|
message(STATUS "Creating virtual env at ${venv_path}")
|
|
execute_process(
|
|
COMMAND "${PYTHON_EXECUTABLE}" -m ensurepip
|
|
RESULT_VARIABLE update_venv_result
|
|
)
|
|
if (NOT update_venv_result EQUAL 0)
|
|
# ensurepip module failed not installed. Check if pip is installed, and cause an error if it's not installed.
|
|
execute_process(
|
|
COMMAND "${PYTHON_EXECUTABLE}" -m pip --version
|
|
RESULT_VARIABLE pip_version_result
|
|
)
|
|
if (NOT pip_version_result EQUAL 0)
|
|
message(FATAL_ERROR "Failed to run ensurepip module, and pip is not installed. Please install pip manually.")
|
|
endif()
|
|
endif()
|
|
|
|
execute_process(
|
|
COMMAND "${PYTHON_EXECUTABLE}" -m pip install --user virtualenv
|
|
RESULT_VARIABLE update_venv_result
|
|
)
|
|
if (NOT update_venv_result EQUAL 0)
|
|
message(FATAL_ERROR "Failed to install virtualenv with pip: result: ${update_venv_result}.")
|
|
endif()
|
|
|
|
execute_process(
|
|
COMMAND "${PYTHON_EXECUTABLE}" -m virtualenv "${venv_path}"
|
|
RESULT_VARIABLE update_venv_result
|
|
)
|
|
if (NOT update_venv_result EQUAL 0)
|
|
message(FATAL_ERROR "Cannot update codegen tool venv: result: ${update_venv_result}.")
|
|
endif()
|
|
|
|
#
|
|
# Install the codegen package with pip
|
|
#
|
|
execute_process(
|
|
COMMAND
|
|
"${venv_path}/${venv_python_executable_path}" -m pip
|
|
install "${CODEGEN_PACKAGE}"
|
|
RESULT_VARIABLE pip_result
|
|
)
|
|
if (NOT pip_result EQUAL 0)
|
|
message(FATAL_ERROR "Failed to install codegen packages into codegen virtualenv. result: ${pip_result}.")
|
|
endif()
|
|
file(TOUCH "${package_dummy}")
|
|
|
|
#
|
|
# Create the dummy file to signify that we have successfully created the venv.
|
|
#
|
|
file(TOUCH "${venv_dummy}")
|
|
endif()
|
|
|
|
#
|
|
# Add build-time target to update the codegen package if it's touched.
|
|
#
|
|
add_custom_command(
|
|
OUTPUT
|
|
"${package_dummy}"
|
|
COMMAND
|
|
"${venv_path}/${venv_python_executable_path}" -m pip
|
|
install "${CODEGEN_PACKAGE}"
|
|
COMMAND
|
|
"${CMAKE_COMMAND}" "-E" "touch" "${package_dummy}"
|
|
DEPENDS
|
|
"${CODEGEN_PACKAGE}"
|
|
)
|
|
|
|
set(include_directories_arguments "")
|
|
foreach(include_directory ${include_directories})
|
|
list(APPEND include_directories_arguments "-I")
|
|
list(APPEND include_directories_arguments "${include_directory}")
|
|
endforeach()
|
|
|
|
message(STATUS "Updating codegen targets")
|
|
|
|
#
|
|
# Set up targets for all files which will be generated by the codegen tool.
|
|
#
|
|
set(all_output_files "")
|
|
foreach(current_source_file ${source_files})
|
|
|
|
#
|
|
# Determine which files are generated by this source file at configure time
|
|
#
|
|
file(RELATIVE_PATH source_file_relative "${input_root}" "${current_source_file}")
|
|
set(cached_source_outputs_filename "${cache_path}/${source_file_relative}.outputs")
|
|
set(cached_outputs_are_ok false)
|
|
|
|
if (EXISTS "${cached_source_outputs_filename}")
|
|
# Get the timestamp of the cache file and the source file
|
|
file(TIMESTAMP "${cached_source_outputs_filename}" cached_outputs_timestamp "%s")
|
|
file(TIMESTAMP "${current_source_file}" source_file_timestamp "%s")
|
|
if (cached_outputs_timestamp GREATER source_file_timestamp)
|
|
# Cache hit
|
|
file(READ "${cached_source_outputs_filename}" current_output_files)
|
|
set(cached_outputs_are_ok true)
|
|
endif()
|
|
endif()
|
|
|
|
if (NOT "${cached_outputs_are_ok}")
|
|
execute_process(
|
|
COMMAND
|
|
"${venv_path}/${venv_python_executable_path}" "-m" "codegen"
|
|
"get_output_files"
|
|
"${current_source_file}"
|
|
${include_directories_arguments}
|
|
"--libclangpath" "${CLANG_LIBRARY}"
|
|
"--source-root" "${input_root}"
|
|
"--output-root" "${output_root}"
|
|
"--cache-path" "${cache_path}"
|
|
OUTPUT_VARIABLE current_output_files
|
|
RESULT_VARIABLE tool_result
|
|
)
|
|
if (NOT tool_result EQUAL 0)
|
|
message(SEND_ERROR "Error running codegen tool. result: ${tool_result}.")
|
|
continue()
|
|
endif()
|
|
endif()
|
|
|
|
|
|
#
|
|
# Set up a build target for the outputs given to us by the above commands.
|
|
#
|
|
string(STRIP "${current_output_files}" current_output_files)
|
|
list(LENGTH current_output_files current_output_files_len)
|
|
if ("${current_output_files_len}" EQUAL 0)
|
|
continue()
|
|
endif()
|
|
|
|
foreach(current_output_file ${current_output_files})
|
|
list(APPEND all_output_files "${current_output_file}")
|
|
endforeach()
|
|
|
|
add_custom_command(
|
|
OUTPUT "${current_output_files}"
|
|
COMMAND
|
|
"${venv_path}/${venv_python_executable_path}" "-m" "codegen"
|
|
"generate"
|
|
"${current_source_file}"
|
|
${include_directories_arguments}
|
|
"--libclangpath" "${CLANG_LIBRARY}"
|
|
"--source-root" "${input_root}"
|
|
"--output-root" "${output_root}"
|
|
DEPENDS "${current_source_file}" "${package_dummy}"
|
|
)
|
|
|
|
endforeach()
|
|
|
|
#
|
|
# "Return" a list of all files that will be generated during build time.
|
|
#
|
|
set("${generated_files_var}" "${all_output_files}" PARENT_SCOPE)
|
|
|
|
endfunction()
|
|
|
|
#
|
|
# gather_include_directories recursively builds a list of include directories
|
|
# across all dependencies.
|
|
#
|
|
|
|
function(_gather_include_directories_impl target_name)
|
|
get_target_property(target_dependencies ${target_name} INTERFACE_LINK_LIBRARIES)
|
|
foreach(dep ${target_dependencies})
|
|
if(TARGET ${dep})
|
|
get_target_property(dep_includes ${dep} INTERFACE_INCLUDE_DIRECTORIES)
|
|
list(APPEND target_includes ${dep_includes})
|
|
_gather_include_directories_impl(${dep})
|
|
endif()
|
|
endforeach()
|
|
set(target_includes ${target_includes} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
function(gather_include_directories var target_name)
|
|
get_target_property(target_includes ${target_name} INTERFACE_INCLUDE_DIRECTORIES)
|
|
_gather_include_directories_impl(${target_name})
|
|
list(REMOVE_DUPLICATES target_includes)
|
|
set(${var} ${target_includes} PARENT_SCOPE)
|
|
endfunction()
|