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>
private import annotation
import mixin
import counter
+import pkgconfig
# Add compiling options
redef class ToolContext
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
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)
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
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)
return
end
- # retreive module
+ # retrieve module
var nmodule = nmoduledecl.parent.as(AModule)
var mmodule = nmodule.mmodule.as(not null)
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
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(" ")}`"
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
-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.
-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.
-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.