diff --git a/coredata.py b/coredata.py index 57db2f9a2..4a56b74a4 100644 --- a/coredata.py +++ b/coredata.py @@ -14,14 +14,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This file contains all data that must persist over multiple -# invocations of Meson. It is roughly the same thing as -# cmakecache. - import pickle version = '0.3.0-research' +# This class contains all data that must persist over multiple +# invocations of Meson. It is roughly the same thing as +# cmakecache. + class CoreData(): def __init__(self, options): @@ -69,3 +69,7 @@ forbidden_target_names = {'clean': None, 'install': None, 'build.ninja': None, } + +class MesonException(Exception): + def __init__(self, *args, **kwargs): + Exception.__init__(args, kwargs) diff --git a/dependencies.py b/dependencies.py index de7ac43ba..141473e91 100644 --- a/dependencies.py +++ b/dependencies.py @@ -14,16 +14,153 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This file contains the detection logic for all those -# packages and frameworks that either don't provide -# a pkg-confg file or require extra functionality -# that can't be expressed with it. +# This file contains the detection logic for external +# dependencies. Mostly just uses pkg-config but also contains +# custom logic for packages that don't provide them. # Currently one file, should probably be split into a # package before this gets too big. -import os, stat, glob +import os, stat, glob, subprocess from interpreter import InvalidArguments +from coredata import MesonException + +class DependencyException(MesonException): + def __init__(self, args, **kwargs): + MesonException.__init__(args, kwargs) + +class Dependency(): + def __init__(self): + pass + + def get_compile_flags(self): + return [] + + def get_link_flags(self): + return [] + + def found(self): + return False + + def get_sources(self): + """Source files that need to be added to the target. + As an example, gtest-all.cc when using GTest.""" + return [] + +class PackageDependency(Dependency): # Custom detector, not pkg-config. + def __init__(self, dep): + Dependency.__init__(self) + self.dep = dep + + def get_link_flags(self): + return self.dep.get_link_flags() + + def get_compile_flags(self): + return self.dep.get_compile_flags() + + def found(self): + return self.dep.found() + + def get_sources(self): + return self.dep.get_sources() + +# This should be an InterpreterObject. Fix it. + +class PkgConfigDependency(Dependency): + pkgconfig_found = False + + def __init__(self, name, required): + Dependency.__init__(self) + if not PkgConfigDependency.pkgconfig_found: + self.check_pkgconfig() + + self.is_found = False + p = subprocess.Popen(['pkg-config', '--modversion', name], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out = p.communicate()[0] + if p.returncode != 0: + if required: + raise DependencyException('Required dependency %s not found.' % name) + self.modversion = 'none' + self.cflags = [] + self.libs = [] + else: + self.is_found = True + self.modversion = out.decode().strip() + p = subprocess.Popen(['pkg-config', '--cflags', name], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out = p.communicate()[0] + if p.returncode != 0: + raise RuntimeError('Could not generate cflags for %s.' % name) + self.cflags = out.decode().split() + + p = subprocess.Popen(['pkg-config', '--libs', name], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out = p.communicate()[0] + if p.returncode != 0: + raise RuntimeError('Could not generate libs for %s.' % name) + self.libs = out.decode().split() + + def get_modversion(self): + return self.modversion + + def get_compile_flags(self): + return self.cflags + + def get_link_flags(self): + return self.libs + + def check_pkgconfig(self): + p = subprocess.Popen(['pkg-config', '--version'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + out = p.communicate()[0] + if p.returncode != 0: + raise RuntimeError('Pkg-config executable not found.') + print('Found pkg-config version %s.' % out.decode().strip()) + PkgConfigDependency.pkgconfig_found = True + + def found(self): + return self.is_found + +class ExternalProgram(): + def __init__(self, name, fullpath=None): + self.name = name + self.fullpath = fullpath + + def found(self): + return self.fullpath is not None + + def get_command(self): + return self.fullpath + + def get_name(self): + return self.name + +class ExternalLibrary(Dependency): + def __init__(self, name, fullpath=None): + Dependency.__init__(self) + self.name = name + self.fullpath = fullpath + + def found(self): + return self.fullpath is not None + + def get_name(self): + return self.name + + def get_link_flags(self): + if self.found(): + return [self.fullpath] + return [] + +def find_external_dependency(name, kwargs): + required = kwargs.get('required', False) + if name in packages: + dep = packages[name](kwargs) + if required and not dep.found(): + raise DependencyException('Dependency "%s" not found' % name) + return PackageDependency(dep) + return PkgConfigDependency(name, required) class BoostDependency(): def __init__(self, kwargs): @@ -145,7 +282,33 @@ class GMockDependency(): fname = os.path.join(self.libdir, self.libname) return os.path.exists(fname) +class Qt5Dependency(): + def __init__(self, kwargs): + self.root = '/usr' + self.modules = [] + for module in kwargs.get('modules', []): + self.modules.append(PkgConfigDependency(module)) + + def get_version(self): + return '1.something_maybe' + + def get_compile_flags(self): + return [] + + def get_sources(self): + return [] + + def get_link_flags(self): + return ['-lgmock'] + + def found(self): + fname = os.path.join(self.libdir, self.libname) + return os.path.exists(fname) + +# This has to be at the end so all classes it references +# are defined. packages = {'boost': BoostDependency, 'gtest': GTestDependency, 'gmock': GMockDependency, + 'qt5': Qt5Dependency, } diff --git a/environment.py b/environment.py index 3eaaa52c2..2d8ae135c 100755 --- a/environment.py +++ b/environment.py @@ -451,147 +451,3 @@ class Environment(): unixdirs += glob('/usr/lib/' + plat + '*') unixdirs.append('/usr/local/lib') return unixdirs - -class Dependency(): - def __init__(self): - pass - - def get_compile_flags(self): - return [] - - def get_link_flags(self): - return [] - - def found(self): - return False - - def get_sources(self): - """Source files that need to be added to the target. - As an example, gtest-all.cc when using GTest.""" - return [] - -class PackageDependency(Dependency): # Custom detector, not pkg-config. - def __init__(self, dep): - Dependency.__init__(self) - self.dep = dep - - def get_link_flags(self): - return self.dep.get_link_flags() - - def get_compile_flags(self): - return self.dep.get_compile_flags() - - def found(self): - return self.dep.found() - - def get_sources(self): - return self.dep.get_sources() - -# This should be an InterpreterObject. Fix it. - -class PkgConfigDependency(Dependency): - pkgconfig_found = False - - def __init__(self, name, required): - Dependency.__init__(self) - if not PkgConfigDependency.pkgconfig_found: - self.check_pkgconfig() - - self.is_found = False - p = subprocess.Popen(['pkg-config', '--modversion', name], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = p.communicate()[0] - if p.returncode != 0: - if required: - raise EnvironmentException('Required dependency %s not found.' % name) - self.modversion = 'none' - self.cflags = [] - self.libs = [] - else: - self.is_found = True - self.modversion = out.decode().strip() - p = subprocess.Popen(['pkg-config', '--cflags', name], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = p.communicate()[0] - if p.returncode != 0: - raise RuntimeError('Could not generate cflags for %s.' % name) - self.cflags = out.decode().split() - - p = subprocess.Popen(['pkg-config', '--libs', name], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = p.communicate()[0] - if p.returncode != 0: - raise RuntimeError('Could not generate libs for %s.' % name) - self.libs = out.decode().split() - - def get_modversion(self): - return self.modversion - - def get_compile_flags(self): - return self.cflags - - def get_link_flags(self): - return self.libs - - def check_pkgconfig(self): - p = subprocess.Popen(['pkg-config', '--version'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = p.communicate()[0] - if p.returncode != 0: - raise RuntimeError('Pkg-config executable not found.') - print('Found pkg-config version %s.' % out.decode().strip()) - PkgConfigDependency.pkgconfig_found = True - - def found(self): - return self.is_found - -class ExternalProgram(): - def __init__(self, name, fullpath=None): - self.name = name - self.fullpath = fullpath - - def found(self): - return self.fullpath is not None - - def get_command(self): - return self.fullpath - - def get_name(self): - return self.name - -class ExternalLibrary(Dependency): - def __init__(self, name, fullpath=None): - Dependency.__init__(self) - self.name = name - self.fullpath = fullpath - - def found(self): - return self.fullpath is not None - - def get_name(self): - return self.name - - def get_link_flags(self): - if self.found(): - return [self.fullpath] - return [] - -def find_external_dependency(name, kwargs): - required = kwargs.get('required', False) - if name in dependencies.packages: - dep = dependencies.packages[name](kwargs) - if required and not dep.found(): - raise EnvironmentException('Dependency "%s" not found' % name) - return PackageDependency(dep) - return PkgConfigDependency(name, required) - -def test_pkg_config(): - name = 'gtk+-3.0' - dep = PkgConfigDependency(name) - print(dep.get_modversion()) - print(dep.get_compile_flags()) - print(dep.get_link_flags()) - -if __name__ == '__main__': - #test_cmd_line_building() - test_pkg_config() diff --git a/interpreter.py b/interpreter.py index 58e07be7d..7375cf806 100755 --- a/interpreter.py +++ b/interpreter.py @@ -18,6 +18,7 @@ import mparser import nodes import environment import coredata +import dependencies import os, sys, platform import shutil @@ -377,11 +378,11 @@ class BuildTarget(InterpreterObject): def add_external_deps(self, deps): for dep in deps: - if not isinstance(dep, environment.Dependency) and\ + if not isinstance(dep, dependencies.Dependency) and\ not isinstance(dep, ExternalLibraryHolder): raise InvalidArguments('Argument is not an external dependency') self.external_deps.append(dep) - if isinstance(dep, environment.Dependency): + if isinstance(dep, dependencies.Dependency): self.process_sourcelist(dep.get_sources()) def get_external_deps(self): @@ -654,7 +655,7 @@ class Interpreter(): self.coredata.ext_progs[exename].found(): return ExternalProgramHolder(self.coredata.ext_progs[exename]) result = shutil.which(exename) # Does .exe appending on Windows. - extprog = environment.ExternalProgram(exename, result) + extprog = dependencies.ExternalProgram(exename, result) progobj = ExternalProgramHolder(extprog) self.coredata.ext_progs[exename] = extprog if required and not progobj.found(): @@ -671,7 +672,7 @@ class Interpreter(): self.coredata.ext_libs[libname].found(): return ExternalLibraryHolder(self.coredata.ext_libs[libname]) result = self.environment.find_library(libname) - extlib = environment.ExternalLibrary(libname, result) + extlib = dependencies.ExternalLibrary(libname, result) libobj = ExternalLibraryHolder(extlib) self.coredata.ext_libs[libname] = extlib if required and not libobj.found(): @@ -684,9 +685,9 @@ class Interpreter(): if name in self.coredata.deps: dep = self.coredata.deps[name] else: - dep = environment.Dependency() # Returns always false for dep.found() + dep = dependencies.Dependency() # Returns always false for dep.found() if not dep.found(): - dep = environment.find_external_dependency(name, kwargs) + dep = dependencies.find_external_dependency(name, kwargs) self.coredata.deps[name] = dep return dep @@ -828,7 +829,7 @@ class Interpreter(): def is_assignable(self, value): if isinstance(value, InterpreterObject) or \ - isinstance(value, environment.Dependency) or\ + isinstance(value, dependencies.Dependency) or\ isinstance(value, nodes.StringStatement) or\ isinstance(value, nodes.BoolStatement) or\ isinstance(value, nodes.IntStatement) or\ diff --git a/meson.py b/meson.py index 9aebc1f8f..a1b785804 100755 --- a/meson.py +++ b/meson.py @@ -15,12 +15,12 @@ # limitations under the License. from optparse import OptionParser -import sys, stat +import sys, stat, traceback import os.path import environment, interpreter import backends, build -from coredata import version +from coredata import version, MesonException usage_info = '%prog [options] source_dir build_dir' @@ -128,7 +128,10 @@ if __name__ == '__main__': try: app.generate() except Exception as e: - print('\nMeson encountered an error:') - print(e) - sys.exit(1) + if isinstance(e, MesonException): + print('\nMeson encountered an error:') + print(e) + sys.exit(1) + else: + traceback.print_exc()