if(NOT DEFINED PP_SHADER_DIR) message(FATAL_ERROR "PP_SHADER_DIR is required") endif() file(REAL_PATH "${PP_SHADER_DIR}" pp_shader_dir) if(NOT IS_DIRECTORY "${pp_shader_dir}") message(FATAL_ERROR "Shader directory does not exist: ${pp_shader_dir}") endif() file(GLOB_RECURSE pp_shader_files "${pp_shader_dir}/*.glsl") if(NOT pp_shader_files) message(FATAL_ERROR "No shader files found under: ${pp_shader_dir}") endif() set(pp_shader_errors "") set(pp_top_level_count 0) set(pp_include_count 0) foreach(pp_shader_file IN LISTS pp_shader_files) file(RELATIVE_PATH pp_shader_rel "${pp_shader_dir}" "${pp_shader_file}") file(READ "${pp_shader_file}" pp_shader_contents) string(REGEX MATCHALL "#[ \t]*include[ \t]+\"[^\"]+\"" pp_include_lines "${pp_shader_contents}") foreach(pp_include_line IN LISTS pp_include_lines) string(REGEX REPLACE ".*\"([^\"]+)\".*" "\\1" pp_include_path "${pp_include_line}") if(pp_include_path MATCHES "^/") list(APPEND pp_shader_errors "${pp_shader_rel}: include path must be relative: ${pp_include_path}") endif() if(pp_include_path MATCHES "^[A-Za-z]:") list(APPEND pp_shader_errors "${pp_shader_rel}: include path must not be drive-absolute: ${pp_include_path}") endif() if(pp_include_path MATCHES "\\.\\.") list(APPEND pp_shader_errors "${pp_shader_rel}: include path must not traverse parent directories: ${pp_include_path}") endif() if(NOT EXISTS "${pp_shader_dir}/${pp_include_path}") list(APPEND pp_shader_errors "${pp_shader_rel}: missing include: ${pp_include_path}") endif() endforeach() if(pp_shader_rel MATCHES "^include/") math(EXPR pp_include_count "${pp_include_count} + 1") if(pp_shader_contents MATCHES "\\[\\[(vertex|fragment)\\]\\]") list(APPEND pp_shader_errors "${pp_shader_rel}: include shaders must not declare stage markers") endif() else() math(EXPR pp_top_level_count "${pp_top_level_count} + 1") string(REGEX MATCHALL "\\[\\[vertex\\]\\]" pp_vertex_markers "${pp_shader_contents}") string(REGEX MATCHALL "\\[\\[fragment\\]\\]" pp_fragment_markers "${pp_shader_contents}") list(LENGTH pp_vertex_markers pp_vertex_count) list(LENGTH pp_fragment_markers pp_fragment_count) if(NOT pp_vertex_count EQUAL 1) list(APPEND pp_shader_errors "${pp_shader_rel}: expected exactly one [[vertex]] marker") endif() if(NOT pp_fragment_count EQUAL 1) list(APPEND pp_shader_errors "${pp_shader_rel}: expected exactly one [[fragment]] marker") endif() string(FIND "${pp_shader_contents}" "[[vertex]]" pp_vertex_pos) string(FIND "${pp_shader_contents}" "[[fragment]]" pp_fragment_pos) if(pp_vertex_pos GREATER_EQUAL 0 AND pp_fragment_pos GREATER_EQUAL 0 AND NOT pp_vertex_pos LESS pp_fragment_pos) list(APPEND pp_shader_errors "${pp_shader_rel}: [[vertex]] marker must appear before [[fragment]]") endif() endif() endforeach() if(pp_shader_errors) list(JOIN pp_shader_errors "\n" pp_shader_error_text) message(FATAL_ERROR "Shader validation failed:\n${pp_shader_error_text}") endif() message(STATUS "Validated ${pp_top_level_count} shader programs and ${pp_include_count} shader includes under ${pp_shader_dir}")