1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
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
25 redef class ToolContext
26 var c_compiler_options_phase
: Phase = new CCompilerOptionsPhase(self, null)
29 private class CCompilerOptionsPhase
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"
36 redef fun process_annotated_node
(nmoduledecl
, nat
)
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
44 # Do some validity checks and print errors if the annotation is used incorrectly
45 var modelbuilder
= toolcontext
.modelbuilder
47 if not nmoduledecl
isa AModuledecl then
48 modelbuilder
.error
(nat
, "Syntax error: only the declaration of modules may use \"{annotation_name}\
".")
54 modelbuilder
.error
(nat
, "Syntax error: \"{annotation_name}\
" expects at least one argument.")
58 var options
= new Array[CCompilerOption]
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\
")`.")
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
)
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.")
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.")
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
)
91 var opt
= new ExecCCompilerOption(exec_args_as_strings
, expr
)
94 modelbuilder
.error
(nat
, "Syntax error: \"{annotation_name}\
" expects its arguments to be the name of the package as String literals.")
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
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
)
113 # wait for its completion
117 var status
= proc
.status
119 modelbuilder
.error
(opt
.exec_node
, "Annotation error: Something went wrong executing the argument of annotation \"{annotation_name}\
", make sure the command is valid.")
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.")
129 simplified_options
.add
(new DirectCCompilerOption(result
))
131 assert opt
isa DirectCCompilerOption
132 simplified_options
.add
(opt
)
137 var mmodule
= nmoduledecl
.parent
.as(AModule).mmodule
.as(not null)
139 for opt
in simplified_options
do
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
)
151 fun process_c_compiler_annotation
(mmodule
: MModule, opt
: String)
153 mmodule
.c_compiler_options
= "{mmodule.c_compiler_options} {opt}"
156 fun process_c_linker_annotation
(mmodule
: MModule, opt
: String)
158 mmodule
.c_linker_options
= "{mmodule.c_linker_options} {opt}"
161 fun process_cpp_compiler_annotation
(mmodule
: MModule, opt
: String)
163 mmodule
.cpp_compiler_options
= "{mmodule.cpp_compiler_options} {opt}"
167 abstract class CCompilerOption
170 class DirectCCompilerOption
171 super CCompilerOption
174 init (opt
: String) do option
= opt
177 class ExecCCompilerOption
178 super CCompilerOption
180 var command
: Array[String]
181 var exec_node
: ACallExpr
183 init (command
: Array[String], exec_node
: ACallExpr)
185 self.command
= command
186 self.exec_node
= exec_node