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 `cflags` and `ldflags` 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
24 private import annotation
25 private import platform
27 redef class ToolContext
28 # Phase to find `cflags`, `ldflags` and `cppflags`
29 var cflags_phase
: Phase = new CCompilerOptionsPhase(self, [platform_phase
])
32 private class CCompilerOptionsPhase
35 fun compiler_annotation_name
: String do return "cflags"
36 fun linker_annotation_name
: String do return "ldflags"
37 fun cpp_compiler_annotation_name
: String do return "cppflags"
39 redef fun process_annotated_node
(nmoduledecl
, nat
)
41 # Skip if we are not interested
42 var annotation_name
= nat
.name
43 if annotation_name
!= compiler_annotation_name
and
44 annotation_name
!= linker_annotation_name
and
45 annotation_name
!= cpp_compiler_annotation_name
then return
47 # Do some validity checks and print errors if the annotation is used incorrectly
48 var modelbuilder
= toolcontext
.modelbuilder
50 if not nmoduledecl
isa AModuledecl then
51 modelbuilder
.error
(nat
, "Syntax Error: only the declaration of modules may use `{annotation_name}`.")
57 modelbuilder
.error
(nat
, "Syntax Error: `{annotation_name}` expects at least one argument.")
61 var options
= new Array[CCompilerOption]
63 if expr
isa AStringFormExpr then
64 var text
= expr
.collect_text
65 text
= text
.substring
(1, text
.length-2
)
66 var opt
= new DirectCCompilerOption(text
)
68 else if expr
isa ACallExpr then
69 # We support calls to "exec" only
70 var exec_args
= expr
.n_args
.to_a
71 if expr
.n_qid
.n_id
.text
!= "exec" or exec_args
.is_empty
then
72 modelbuilder
.error
(nat
, "Syntax Error: `{annotation_name}` accepts only calls to `exec` with the command as arguments.")
76 var exec_args_as_strings
= new Array[String]
77 for exec_arg
in exec_args
do
78 if not exec_arg
isa AStringFormExpr then
79 modelbuilder
.error
(nat
, "Syntax Error: calls to `exec` expects the arguments to be String literals.")
82 var arg_string
= exec_arg
.collect_text
83 arg_string
= arg_string
.substring
(1, arg_string
.length-2
)
84 exec_args_as_strings
.add
(arg_string
)
88 var opt
= new ExecCCompilerOption(exec_args_as_strings
, expr
)
91 modelbuilder
.error
(nat
, "Syntax Error: `{annotation_name}` expects its arguments to be the name of the package as String literals.")
96 # process calls to external command
97 var simplified_options
= new Array[DirectCCompilerOption]
99 if opt
isa ExecCCompilerOption then
100 # prepare to execute command
101 var cmd_args
= opt
.command
103 if cmd_args
.length
== 1 then
104 proc
= new ProcessReader.from_a
(cmd_args
[0], new Array[String])
105 else if cmd_args
.length
> 1 then
106 var rest_args
= cmd_args
.subarray
(1, cmd_args
.length-1
)
107 proc
= new ProcessReader.from_a
(cmd_args
[0], rest_args
)
110 # wait for its completion
114 var status
= proc
.status
116 modelbuilder
.error
(opt
.exec_node
, "Error: something went wrong when executing the argument of `{annotation_name}`, make sure the command is valid.")
121 var result
= proc
.read_all
.replace
("\n", " ")
122 if result
.is_empty
then
123 modelbuilder
.error
(opt
.exec_node
, "Error: got no result from the command, make sure it is valid.")
126 simplified_options
.add
(new DirectCCompilerOption(result
))
128 assert opt
isa DirectCCompilerOption
129 simplified_options
.add
(opt
)
134 var mmodule
= nmoduledecl
.parent
.as(AModule).mmodule
.as(not null)
136 # Get target platform from annotation on annotation
139 ## Is there an imported platform?
140 var target_platform
= mmodule
.target_platform
141 if target_platform
!= null then
142 platform
= target_platform
.name
or else ""
145 ## Is the platform declared explicitly?
146 var annots
= nat
.n_annotations
147 if annots
!= null then
148 var items
= annots
.n_items
149 if items
.length
> 1 then
150 modelbuilder
.error
(annots
, "Syntax Error: `{annotation_name}` accepts only a single annotation, the platform name.")
153 assert items
.length
== 1
155 var item
= items
.first
159 # Store the flags in the module
160 for opt
in simplified_options
do
162 if annotation_name
== compiler_annotation_name
then
163 mmodule
.cflags
.add_one
(platform
, arg
)
164 else if annotation_name
== linker_annotation_name
then
165 mmodule
.ldflags
.add_one
(platform
, arg
)
166 else if annotation_name
== cpp_compiler_annotation_name
then
167 mmodule
.cppflags
.add_one
(platform
, arg
)
174 abstract class CCompilerOption
177 class DirectCCompilerOption
178 super CCompilerOption
183 class ExecCCompilerOption
184 super CCompilerOption
186 var command
: Array[String]
187 var exec_node
: ACallExpr