Merge: nitc: check pkg-config packages availability later
authorJean Privat <jean@pryen.org>
Fri, 26 Jan 2018 21:12:15 +0000 (16:12 -0500)
committerJean Privat <jean@pryen.org>
Fri, 26 Jan 2018 21:12:15 +0000 (16:12 -0500)
The `pkgconfig` annotation tells the compiler to use the `pkg-config` command in order to get the C compiler and linker options required to build the user C code. The availability of each package was tested twice, once at reading the annotation and once in the generated Makefile. This PR moves the first check later, with the generation of the Makefile. There is still two checks, one by the Nit compiler with a prettier output, and one by the Makefile in case it is distributed with the generated C source files.

This fixes an issue we've had when compiling for Android and iOS, where the result of the annotation was not used but still blocked the compilation if the host did not have the package. And leaving the check to the Makefile give a chance to the programmer to tweak it before the C compilation when cross-compiling.

The error message thrown by the Makefile is less user-friendly as we lose the reference to the Nit source file and line number. This should not be much on an issue since it is rarely a programming error, more of a system configuration error, but it could be improved upon if it becomes an issue.

Pull-Request: #2610
Reviewed-by: Jean Privat <jean@pryen.org>

src/compiler/abstract_compiler.nit
src/ffi/pkgconfig.nit
src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit
src/platform/platform.nit
tests/sav/error_annot_pkgconfig_alt0.res
tests/sav/error_annot_pkgconfig_alt1.res
tests/sav/test_syntax.res

index a3a0bc8..1a1bb07 100644 (file)
@@ -24,6 +24,7 @@ import c_tools
 private import annotation
 import mixin
 import counter
+import pkgconfig
 
 # Add compiling options
 redef class ToolContext
@@ -188,6 +189,8 @@ class MakefileToolchain
                var time1 = get_time
                self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
 
+               if not toolcontext.check_errors then return
+
                # Execute the Makefile
 
                if self.toolcontext.opt_no_cc.value then return
@@ -466,14 +469,19 @@ endif
                        f.close
                end
 
-               var java_files = new Array[ExternFile]
-
+               # pkg-config annotation support
                var pkgconfigs = new Array[String]
                for f in compiler.extern_bodies do
                        pkgconfigs.add_all f.pkgconfigs
                end
-               # Protect pkg-config
+
+               # Only test if pkg-config is used
                if not pkgconfigs.is_empty then
+
+                       # Check availability of pkg-config, silence the proc output
+                       toolcontext.check_pkgconfig_packages pkgconfigs
+
+                       # Double the check in the Makefile in case it's distributed
                        makefile.write """
 # does pkg-config exists?
 ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
@@ -491,6 +499,7 @@ endif
                end
 
                # Compile each required extern body into a specific .o
+               var java_files = new Array[ExternFile]
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
                        var ff = f.filename.basename
index 453ddfc..74dc8fc 100644 (file)
@@ -23,11 +23,48 @@ private import annotation
 private import literal
 
 redef class ToolContext
+       # Detects the `pkgconfig` annotation on the module declaration only
        var pkgconfig_phase: Phase = new PkgconfigPhase(self, [literal_phase])
+
+       # Is the external program `pkg-config` available?
+       var pkgconfig_is_available: Bool is lazy do
+               # Ignore/silence the process output
+               var proc_which = new ProcessReader("which", "pkg-config")
+               proc_which.wait
+
+               var status = proc_which.status
+               if status != 0 then
+                       error(null, "Error: program `pkg-config` not found, make sure it is installed.")
+                       return false
+               end
+               return true
+       end
+
+       # Check if the `packages` are known by the external program `pkg-config`
+       #
+       # Missing packages are reported to the console via `ToolContext::error`.
+       # Check for errors using `check_errors`.
+       fun check_pkgconfig_packages(packages: Array[String])
+       do
+               if not pkgconfig_is_available then return
+
+               for pkg in packages do
+                       var proc_exist = new Process("pkg-config", "--exists", pkg)
+                       proc_exist.wait
+                       var status = proc_exist.status
+                       if status == 1 then
+                               error(null,
+                                       "Error: dev package for `{pkg}` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.")
+                       else if status != 0 then
+                               error(null,
+                                       "Error: something went wrong calling `pkg-config`, make sure it is correctly configured.")
+                       end
+               end
+       end
 end
 
-# Detects the `pkgconfig` annotation on the module declaration only.
-class PkgconfigPhase
+# Detects the `pkgconfig` annotation on the module declaration only
+private class PkgconfigPhase
        super Phase
 
        redef fun process_annotated_node(nmoduledecl, nat)
@@ -43,7 +80,7 @@ class PkgconfigPhase
                        return
                end
 
-               # retreive module
+               # retrieve module
                var nmodule = nmoduledecl.parent.as(AModule)
                var mmodule = nmodule.mmodule.as(not null)
 
@@ -66,27 +103,7 @@ class PkgconfigPhase
                        end
                end
 
-               # check availability of pkg-config
-               var proc_which = new ProcessReader("which", "pkg-config")
-               proc_which.wait
-               var status = proc_which.status
-               if status != 0 then
-                       modelbuilder.error(nat, "Error: program `pkg-config` not found, make sure it is installed.")
-                       return
-               end
-
                for pkg in pkgs do
-                       var proc_exist = new Process("pkg-config", "--exists", pkg)
-                       proc_exist.wait
-                       status = proc_exist.status
-                       if status == 1 then
-                               modelbuilder.error(nat, "Error: dev package for `{pkg}` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.")
-                               continue
-                       else if status != 0 then
-                               modelbuilder.error(nat, "Error: something went wrong calling `pkg-config`, make sure it is correctly installed.")
-                               continue
-                       end
-
                        mmodule.pkgconfigs.add pkg
                end
        end
index 3ac05af..44f6854 100644 (file)
@@ -138,19 +138,10 @@ redef class AModule
                var pkgconfigs = mmodule.pkgconfigs
                var pkg_cflags = ""
                if not pkgconfigs.is_empty then
-                       var cmd = "which pkg-config >/dev/null"
-                       if system(cmd) != 0 then
-                               v.fatal "FFI Error: Command `pkg-config` not found. Please install it"
-                               return false
-                       end
 
-                       for p in pkgconfigs do
-                               cmd = "pkg-config --exists '{p}'"
-                               if system(cmd) != 0 then
-                                       v.fatal "FFI Error: package {p} is not found by `pkg-config`. Please install it."
-                                       return false
-                               end
-                       end
+                       # Check if the pkgconfig packages are available
+                       v.modelbuilder.toolcontext.check_pkgconfig_packages pkgconfigs
+                       if not v.modelbuilder.toolcontext.check_errors then return false
 
                        pkg_cflags = "`pkg-config --cflags {pkgconfigs.join(" ")}`"
                        ldflags += " `pkg-config --libs {pkgconfigs.join(" ")}`"
index 8fafcd1..3aec2f5 100644 (file)
@@ -23,8 +23,10 @@ private import parser_util
 private import annotation
 
 redef class ToolContext
+       # Detects the `platform` annotation to set a mobile target platform
        var platform_phase: Phase = new PlatformPhase(self, [modelize_property_phase])
 
+       # Get platform compilation settings from its `name`
        protected fun platform_from_name(name: String): nullable Platform
        do
                return null
index c8148e7..27eaad6 100644 (file)
@@ -1 +1 @@
-alt/error_annot_pkgconfig_alt0.nit:17,38--46: Error: dev package for `error_annot_pkgconfig_alt0` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
+Error: dev package for `error_annot_pkgconfig_alt0` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
index 4fb9358..2f724ff 100644 (file)
@@ -1,2 +1,2 @@
-alt/error_annot_pkgconfig_alt1.nit:18,38--82: Error: dev package for `missing-lib` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
-alt/error_annot_pkgconfig_alt1.nit:18,38--82: Error: dev package for `other missing lib` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
+Error: dev package for `missing-lib` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
+Error: dev package for `other missing lib` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
index 98bfc04..0b4f9cd 100644 (file)
@@ -1 +1,2 @@
-test_syntax.nit:48,2--39: Error: dev package for `a libray from outer space` unknown by `pkg-config`, install it with `apt-get`, `brew` or similar.
+test_syntax.nit:68,8--27: Warning: superfluous super-class `Object` in class `Blurry`.
+test_syntax.nit:134,8--13: Type Error: `Blurry[ELEM_ENT: nullable Object]` is a generic class.