pep8analysis: intro a variant main program for a web interface
[nit.git] / src / common_ffi / c_compiler_options.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 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 # Offers the annotations `c_compiler_option` and `c_linker_option` to specify
18 # options for the C compiler directly or indirectly. Differs from the `pkgconfig`
19 # annotation by the separation of the options between the compiler and linker.
20 module c_compiler_options
21
22 import c
23 import cpp
24
25 redef class ToolContext
26 var c_compiler_options_phase: Phase = new CCompilerOptionsPhase(self, null)
27 end
28
29 private class CCompilerOptionsPhase
30 super Phase
31
32 fun compiler_annotation_name: String do return "c_compiler_option"
33 fun linker_annotation_name: String do return "c_linker_option"
34 fun cpp_compiler_annotation_name: String do return "cpp_compiler_option"
35
36 redef fun process_annotated_node(nmoduledecl, nat)
37 do
38 # Skip if we are not interested
39 var annotation_name = nat.n_atid.n_id.text
40 if annotation_name != compiler_annotation_name and
41 annotation_name != linker_annotation_name and
42 annotation_name != cpp_compiler_annotation_name then return
43
44 # Do some validity checks and print errors if the annotation is used incorrectly
45 var modelbuilder = toolcontext.modelbuilder
46
47 if not nmoduledecl isa AModuledecl then
48 modelbuilder.error(nat, "Syntax error: only the declaration of modules may use \"{annotation_name}\".")
49 return
50 end
51
52 var args = nat.n_args
53 if args.is_empty then
54 modelbuilder.error(nat, "Syntax error: \"{annotation_name}\" expects at least one argument.")
55 return
56 end
57
58 var options = new Array[CCompilerOption]
59 for arg in args do
60 if not arg isa AExprAtArg then
61 modelbuilder.error(nat, "Syntax error: \"{annotation_name}\" expects its arguments to be the name of the package as String literals or a call to `exex(\"local_program\")`.")
62 return
63 end
64
65 var expr = arg.n_expr
66 if expr isa AStringFormExpr then
67 var text = expr.collect_text
68 text = text.substring(1, text.length-2)
69 var opt = new DirectCCompilerOption(text)
70 options.add(opt)
71 else if expr isa ACallExpr then
72 # We support calls to "exec" only
73 var exec_args = expr.n_args.to_a
74 if expr.n_id.text != "exec" or exec_args.is_empty then
75 modelbuilder.error(nat, "Syntax error: \"{annotation_name}\" accepts only calls to `exec` with the command as arguments.")
76 return
77 end
78
79 var exec_args_as_strings = new Array[String]
80 for exec_arg in exec_args do
81 if not exec_arg isa AStringFormExpr then
82 modelbuilder.error(nat, "Syntax error: calls to `exec` expects the arguments to be String literals.")
83 return
84 else
85 var arg_string = exec_arg.collect_text
86 arg_string = arg_string.substring(1, arg_string.length-2)
87 exec_args_as_strings.add(arg_string)
88 end
89 end
90
91 var opt = new ExecCCompilerOption(exec_args_as_strings, expr)
92 options.add(opt)
93 else
94 modelbuilder.error(nat, "Syntax error: \"{annotation_name}\" expects its arguments to be the name of the package as String literals.")
95 return
96 end
97 end
98
99 # process calls to external command
100 var simplified_options = new Array[DirectCCompilerOption]
101 for opt in options do
102 if opt isa ExecCCompilerOption then
103 # prepare to execute command
104 var cmd_args = opt.command
105 var proc
106 if cmd_args.length == 1 then
107 proc = new IProcess.from_a(cmd_args[0], new Array[String])
108 else if cmd_args.length > 1 then
109 var rest_args = cmd_args.subarray(1, cmd_args.length-1)
110 proc = new IProcess.from_a(cmd_args[0], rest_args)
111 else abort
112
113 # wait for its completion
114 proc.wait
115
116 # check result
117 var status = proc.status
118 if status != 0 then
119 modelbuilder.error(opt.exec_node, "Annotation error: Something went wrong executing the argument of annotation \"{annotation_name}\", make sure the command is valid.")
120 return
121 end
122
123 # process result
124 var result = proc.read_all.replace("\n", " ")
125 if result.is_empty then
126 modelbuilder.error(opt.exec_node, "Annotation error: Got no result from the command, make sure it is valid.")
127 return
128 end
129 simplified_options.add(new DirectCCompilerOption(result))
130 else
131 assert opt isa DirectCCompilerOption
132 simplified_options.add(opt)
133 end
134 end
135
136 # retreive module
137 var mmodule = nmoduledecl.parent.as(AModule).mmodule.as(not null)
138
139 for opt in simplified_options do
140 var cmd = opt.option
141 if annotation_name == compiler_annotation_name then
142 process_c_compiler_annotation(mmodule, cmd)
143 else if annotation_name == linker_annotation_name then
144 process_c_linker_annotation(mmodule, cmd)
145 else if annotation_name == cpp_compiler_annotation_name then
146 process_cpp_compiler_annotation(mmodule, cmd)
147 else abort
148 end
149 end
150
151 fun process_c_compiler_annotation(mmodule: MModule, opt: String)
152 do
153 mmodule.c_compiler_options = "{mmodule.c_compiler_options} {opt}"
154 end
155
156 fun process_c_linker_annotation(mmodule: MModule, opt: String)
157 do
158 mmodule.c_linker_options = "{mmodule.c_linker_options} {opt}"
159 end
160
161 fun process_cpp_compiler_annotation(mmodule: MModule, opt: String)
162 do
163 mmodule.cpp_compiler_options = "{mmodule.cpp_compiler_options} {opt}"
164 end
165 end
166
167 abstract class CCompilerOption
168 end
169
170 class DirectCCompilerOption
171 super CCompilerOption
172
173 var option: String
174 init (opt: String) do option = opt
175 end
176
177 class ExecCCompilerOption
178 super CCompilerOption
179
180 var command: Array[String]
181 var exec_node: ACallExpr
182
183 init (command: Array[String], exec_node: ACallExpr)
184 do
185 self.command = command
186 self.exec_node = exec_node
187 end
188 end