nitc & niti: move the duplicated pkg-config checks to the pkgconfig module
[nit.git] / src / ffi / pkgconfig.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 `PkgconfigPhase` to use the external program "pkg-config" in order
18 # to discover what options to pass to the C or C++ compiler.
19 module pkgconfig
20
21 import c
22 private import annotation
23 private import literal
24
25 redef class ToolContext
26 # Detects the `pkgconfig` annotation on the module declaration only
27 var pkgconfig_phase: Phase = new PkgconfigPhase(self, [literal_phase])
28
29 # Is the external program `pkg-config` available?
30 var pkgconfig_is_available: Bool is lazy do
31 # Ignore/silence the process output
32 var proc_which = new ProcessReader("which", "pkg-config")
33 proc_which.wait
34
35 var status = proc_which.status
36 if status != 0 then
37 error(null, "Error: program `pkg-config` not found, make sure it is installed.")
38 return false
39 end
40 return true
41 end
42
43 # Check if the `packages` are known by the external program `pkg-config`
44 #
45 # Missing packages are reported to the console via `ToolContext::error`.
46 # Check for errors using `check_errors`.
47 fun check_pkgconfig_packages(packages: Array[String])
48 do
49 if not pkgconfig_is_available then return
50
51 for pkg in packages do
52 var proc_exist = new Process("pkg-config", "--exists", pkg)
53 proc_exist.wait
54 var status = proc_exist.status
55 if status == 1 then
56 error(null,
57 "Error: dev package for `{pkg}` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.")
58 else if status != 0 then
59 error(null,
60 "Error: something went wrong calling `pkg-config`, make sure it is correctly configured.")
61 end
62 end
63 end
64 end
65
66 # Detects the `pkgconfig` annotation on the module declaration only
67 private class PkgconfigPhase
68 super Phase
69
70 redef fun process_annotated_node(nmoduledecl, nat)
71 do
72 # Skip if we are not interested
73 if nat.name != "pkgconfig" then return
74
75 # Do some validity checks and print errors if the annotation is used incorrectly
76 var modelbuilder = toolcontext.modelbuilder
77
78 if not nmoduledecl isa AModuledecl then
79 modelbuilder.error(nat, "Syntax Error: only the declaration of modules may use `pkgconfig`.")
80 return
81 end
82
83 # retrieve module
84 var nmodule = nmoduledecl.parent.as(AModule)
85 var mmodule = nmodule.mmodule.as(not null)
86
87 # target pkgs
88 var pkgs = new Array[String]
89
90 var args = nat.n_args
91 if args.is_empty then
92 # use module name
93 pkgs.add(mmodule.name)
94 else
95 for arg in args do
96 var pkg = arg.as_string
97 if pkg == null then
98 modelbuilder.error(nat, "Syntax Error: `pkgconfig` expects its arguments to be the name of the package as String literals.")
99 return
100 end
101
102 pkgs.add(pkg)
103 end
104 end
105
106 for pkg in pkgs do
107 mmodule.pkgconfigs.add pkg
108 end
109 end
110 end