# Copyright 2020 The Dawn & Tint Authors # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Check for Jinja2 if (NOT DAWN_JINJA2_DIR) message(STATUS "Dawn: Using system jinja2") execute_process( COMMAND ${Python3_EXECUTABLE} -c "import jinja2" RESULT_VARIABLE RET ) if (NOT RET EQUAL 0) message(FATAL_ERROR "Dawn: Missing dependencies for code generation, please ensure you have python-jinja2 installed.") endif() else() message(STATUS "Dawn: using jinja2 at ${DAWN_JINJA2_DIR}") message(STATUS "Dawn: using markupsafe at ${DAWN_MARKUPSAFE_DIR}") endif() # Function to invoke a generator_lib.py generator. # - SCRIPT is the name of the script to call # - OUTPUT_HEADERS will be modified to contain the list of header files (*.h, *.hpp) generated by this generator # - OUTPUT_SOURCES will be modified to contain the list of all other files generated by this generator # - ARGS are the extra arguments to pass to the script in addition to the base generator_lib.py arguments # - PRINT_NAME is the name to use when outputting status or errors function(DawnGenerator) cmake_parse_arguments(PARSE_ARGV 0 arg "" "SCRIPT;OUTPUT_HEADERS;OUTPUT_SOURCES;OUTPUT_JSONS;OUTPUT_JS;PRINT_NAME" "EXTRA_PARAMETERS" ) message(STATUS "Dawn: Configuring DawnGenerator for ${arg_PRINT_NAME}.") if (arg_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unparsed arguments for DawnGenerator: " "${arg_UNPARSED_ARGUMENTS}") endif () # Build the set of args common to all invocation of that generator. set(BASE_ARGS ${Python3_EXECUTABLE} ${arg_SCRIPT} --template-dir "${DAWN_TEMPLATE_DIR}" --root-dir "${Dawn_SOURCE_DIR}" --output-dir "${DAWN_BUILD_GEN_DIR}" ${arg_EXTRA_PARAMETERS} ) if (DAWN_JINJA2_DIR) list(APPEND BASE_ARGS --jinja2-path ${DAWN_JINJA2_DIR}) endif() if (DAWN_MARKUPSAFE_DIR) list(APPEND BASE_ARGS --markupsafe-path ${DAWN_MARKUPSAFE_DIR}) endif() # Call the generator to get the list of its dependencies. execute_process( COMMAND ${BASE_ARGS} --print-cmake-dependencies OUTPUT_VARIABLE DEPENDENCIES RESULT_VARIABLE RET ) if (NOT RET EQUAL 0) message(FATAL_ERROR "Dawn: Failed to get the dependencies for ${arg_PRINT_NAME}. Base args are '${BASE_ARGS}'.") endif() # Ask CMake to re-run if any of the dependencies changed as it might modify the build graph. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${DEPENDENCIES}) # Call the generator to get the list of its outputs. execute_process( COMMAND ${BASE_ARGS} --print-cmake-outputs OUTPUT_VARIABLE OUTPUTS RESULT_VARIABLE RET ) if (NOT RET EQUAL 0) message(FATAL_ERROR "Dawn: Failed to get the outputs for ${arg_PRINT_NAME}. Base args are '${BASE_ARGS}'.") endif() # Add the custom command that calls the generator. add_custom_command( COMMAND ${BASE_ARGS} DEPENDS ${DEPENDENCIES} OUTPUT ${OUTPUTS} COMMENT "Dawn: Generating files for ${arg_PRINT_NAME}." ) # Populate the list of outputs. set(headers) set(sources) set(jsons) set(js) foreach (filename IN LISTS OUTPUTS) get_filename_component(extension "${filename}" EXT) if (extension MATCHES "(h|hpp)") list(APPEND headers "${filename}") elseif (extension MATCHES "(json)") list(APPEND jsons "${filename}") elseif (extension MATCHES "(js)") list(APPEND js "${filename}") else () list(APPEND sources "${filename}") endif () endforeach () if (sources AND NOT arg_OUTPUT_SOURCES) message(FATAL_ERROR "This function generated source files, although an output variable was not defined!" "Please provide a variable name for OUTPUT_SOURCES.") endif () if (headers AND NOT arg_OUTPUT_HEADERS) message(FATAL_ERROR "This function generated header files, although an output variable was not defined!" "Please provide a variable name for OUTPUT_HEADERS.") endif () if (jsons AND NOT arg_OUTPUT_JSONS) message(FATAL_ERROR "This function generated JSON files, although an output variable was not defined!" "Please provide a variable name for OUTPUT_JSONS.") endif () if (js AND NOT arg_OUTPUT_JS) message(FATAL_ERROR "This function generated JS files, although an output variable was not defined!" "Please provide a variable name for OUTPUT_JS.") endif () set(${arg_OUTPUT_SOURCES} ${sources} PARENT_SCOPE) set(${arg_OUTPUT_HEADERS} ${headers} PARENT_SCOPE) set(${arg_OUTPUT_JSONS} ${jsons} PARENT_SCOPE) set(${arg_OUTPUT_JS} ${js} PARENT_SCOPE) # Prior to CMake 3.20 the GENERATED property is local to a directory which means that putting # generated headers in INTERFACE properties causes dependent targets to complain that they # cannot find the file. (because they don't see it as generated and want to check is is # actually on the filesystem). # Work around this by generating the files once if they aren't present at configuration time. set(needToGenerate OFF) foreach(path ${OUTPUTS}) if (NOT EXISTS ${path}) set(needToGenerate ON) endif() endforeach() if (${needToGenerate}) message(STATUS "Dawn: Generating initial versions of files for ${arg_PRINT_NAME}.") execute_process(COMMAND ${BASE_ARGS} RESULT_VARIABLE RET) if (NOT RET EQUAL 0) message(FATAL_ERROR "Dawn: Failed to generate the initial version of files for ${arg_PRINT_NAME}. Base args are '${BASE_ARGS}'.") endif() endif() endfunction() # Helper function to call dawn_generator.py: # - TARGET is the generator target to build # - OUTPUT_HEADERS, OUTPUT_SOURCES, and PRINT_NAME are like for DawnGenerator function(DawnJSONGenerator) cmake_parse_arguments(PARSE_ARGV 0 arg "" "TARGET;OUTPUT_SOURCES;OUTPUT_HEADERS;OUTPUT_JSONS;OUTPUT_JS;PRINT_NAME" "" ) DawnGenerator( SCRIPT "${Dawn_SOURCE_DIR}/generator/dawn_json_generator.py" PRINT_NAME "${arg_PRINT_NAME}" OUTPUT_HEADERS HEADERS OUTPUT_SOURCES SOURCES OUTPUT_JSONS JSONS OUTPUT_JS JS EXTRA_PARAMETERS --dawn-json "${Dawn_SOURCE_DIR}/src/dawn/dawn.json" --wire-json "${Dawn_SOURCE_DIR}/src/dawn/dawn_wire.json" --kotlin-json "${Dawn_SOURCE_DIR}/src/dawn/dawn_kotlin.json" --targets ${arg_TARGET} ${arg_UNPARSED_ARGUMENTS} ) if (SOURCES AND NOT arg_OUTPUT_SOURCES) message(FATAL_ERROR "This function generated source files, although an output variable was not defined! " "Please provide a variable name for OUTPUT_SOURCES.") elseif (arg_OUTPUT_SOURCES AND NOT SOURCES) message(WARNING "This function did not generate source files. The OUTPUT_SOURCES argument is unnecessary.") endif () if (HEADERS AND NOT arg_OUTPUT_HEADERS) message(FATAL_ERROR "This function generated header files, although an output variable was not defined! " "Please provide a variable name for OUTPUT_HEADERS.") elseif (arg_OUTPUT_HEADERS AND NOT HEADERS) message(WARNING "This function did not generate header files. The OUTPUT_HEADERS argument is unnecessary.") endif () if (JSONS AND NOT arg_OUTPUT_JSONS) message(FATAL_ERROR "This function generated JSON files, although an output variable was not defined! " "Please provide a variable name for OUTPUT_JSONS.") elseif (arg_OUTPUT_JSONS AND NOT JSONS) message(WARNING "This function did not generate JSON files. The OUTPUT_JSONS argument is unnecessary.") endif () if (JS AND NOT arg_OUTPUT_JS) message(FATAL_ERROR "This function generated JS files, although an output variable was not defined! " "Please provide a variable name for OUTPUT_JS.") elseif (arg_OUTPUT_JS AND NOT JS) message(WARNING "This function did not generate JS files. The OUTPUT_JS argument is unnecessary.") endif () # Forward the result up one more scope set(${arg_OUTPUT_SOURCES} ${SOURCES} PARENT_SCOPE) set(${arg_OUTPUT_HEADERS} ${HEADERS} PARENT_SCOPE) set(${arg_OUTPUT_JSONS} ${JSONS} PARENT_SCOPE) set(${arg_OUTPUT_JS} ${JS} PARENT_SCOPE) endfunction()