692d918ab2351af9d1c2b41641b6115af600b94d
[nit.git] / src / frontend / glsl_validation.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Check shader code within Nit modules using the tool _glslangValidator_
18 #
19 # For this phase to work, _glslangValidator_ must be in PATH. It can be
20 # downloaded from https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/
21 module glsl_validation
22
23 import literal
24
25 redef class ToolContext
26 # Shader code validation phase
27 var glsl_validation_phase: Phase = new GLSLValidationPhase(self, [literal_phase])
28 end
29
30 private class GLSLValidationPhase
31 super Phase
32
33 # Annotation names
34
35 fun annot_name_vertex: String do return "glsl_vertex_shader"
36 fun annot_name_fragment: String do return "glsl_fragment_shader"
37
38 # TODO support more shader types as needed
39
40 # Is the tool _glsllangValidator_ in path?
41 var tool_is_in_path: nullable Bool = null
42
43 redef fun process_annotated_node(nstring, nat)
44 do
45 var annot_name = nat.n_atid.n_id.text
46 var is_vertex = annot_name == annot_name_vertex
47 var is_fragment = annot_name == annot_name_fragment
48
49 # Skip if we are not interested
50 if not is_vertex and not is_fragment then return
51
52 # Only applicable on strings
53 if not nstring isa AStringFormExpr then
54 toolcontext.error(nstring.location,
55 "Syntax error: only a string literal can be annotated as \"{annot_name}\".")
56 return
57 end
58
59 # Do not double check if tool is in path
60 var in_path = tool_is_in_path
61 if in_path != null then
62 if not in_path then return
63 else
64 # Is _glslangValidator_ installed?
65 var proc_which = new ProcessReader("which", "glslangValidator")
66 proc_which.wait
67 proc_which.close
68 var status = proc_which.status
69 in_path = status == 0
70 tool_is_in_path = in_path
71 if not in_path then
72 toolcontext.warning(nat.location, "glslvalidator",
73 "Warning: program \"glslangValidator\" not in PATH, cannot validate this shader.")
74 return
75 end
76 end
77
78 # Get the shader source
79 var shader = nstring.value
80 assert shader != null
81
82 # Copy the shader to a file
83 # TODO make it more portable
84 var tmp = "/tmp/"
85 var ext
86 if is_vertex then
87 ext = "vert"
88 else ext = "frag"
89 var path = tmp / "nit_shader." + ext
90
91 shader.write_to_file path
92
93 # Execute the validator
94 var proc_validator = new ProcessReader("glslangValidator", path)
95 proc_validator.wait
96 var lines = proc_validator.read_all.split('\n')
97 proc_validator.close
98
99 # Parse errors
100 var regex = "[A-Z]+: ([0-9]+):([0-9]+): (.*)".to_re
101 for line in lines do
102 var match = line.search(regex)
103
104 # Does it match an error?
105 # If not, then it should be the summary
106 if match != null then
107 var shader_line_no = match.subs[1].to_s.to_i
108 var msg = match.subs[2].to_s
109
110 var line_start = nstring.location.line_start + shader_line_no
111 var char_start = 0
112 var char_end = 0
113 var loc = new Location(nat.location.file,
114 line_start, line_start,
115 char_start, char_end)
116
117 toolcontext.warning(loc, "glslvalidator",
118 "Shader error on {msg}")
119 end
120 end
121 end
122 end