1#
2# Copyright 2013, 2018 Free Software Foundation, Inc.
3#
4# This file is part of GNU Radio
5#
6# GNU Radio is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3, or (at your option)
9# any later version.
10#
11# GNU Radio is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with GNU Radio; see the file COPYING.  If not, write to
18# the Free Software Foundation, Inc., 51 Franklin Street,
19# Boston, MA 02110-1301, USA.
20#
21""" Returns information about a module """
22
23from __future__ import print_function
24from __future__ import absolute_import
25from __future__ import unicode_literals
26
27import os
28
29from ..tools import get_modname
30from .base import ModTool, ModToolException
31
32
33class ModToolInfo(ModTool):
34    """ Return information about a given module """
35    name = 'info'
36    description = 'Return information about a given module.'
37
38    def __init__(self, python_readable=False, suggested_dirs=None, **kwargs):
39        ModTool.__init__(self, **kwargs)
40        # Don't call ModTool._validate(), is is too chatty!
41        self._directory = self.dir
42        self._python_readable = python_readable
43        self._suggested_dirs = suggested_dirs
44
45    def run(self):
46        """ Go, go, go! """
47        mod_info = dict()
48        mod_info['base_dir'] = self._get_base_dir(self._directory)
49        if mod_info['base_dir'] is None:
50            raise ModToolException('{}' if self._python_readable else "No module found.")
51        os.chdir(mod_info['base_dir'])
52        mod_info['modname'] = get_modname()
53        if mod_info['modname'] is None:
54            raise ModToolException('{}' if self._python_readable else "No module found.")
55        if self.info['version'] == '36' and (
56                os.path.isdir(os.path.join('include', mod_info['modname'])) or
57                os.path.isdir(os.path.join('include', 'gnuradio', mod_info['modname']))
58                ):
59            self.info['version'] = '37'
60        if not os.path.isfile(os.path.join('cmake', 'Modules', 'FindCppUnit.cmake')):
61            self.info['version'] = '38'
62        mod_info['version'] = self.info['version']
63        if 'is_component' in list(self.info.keys()) and self.info['is_component']:
64            mod_info['is_component'] = True
65        mod_info['incdirs'] = []
66        mod_incl_dir = os.path.join(mod_info['base_dir'], 'include')
67        if os.path.isdir(os.path.join(mod_incl_dir, mod_info['modname'])):
68            mod_info['incdirs'].append(os.path.join(mod_incl_dir, mod_info['modname']))
69        else:
70            mod_info['incdirs'].append(mod_incl_dir)
71        build_dir = self._get_build_dir(mod_info)
72        if build_dir is not None:
73            mod_info['build_dir'] = build_dir
74            mod_info['incdirs'] += self._get_include_dirs(mod_info)
75        if self._python_readable:
76            print(str(mod_info))
77        else:
78            self._pretty_print(mod_info)
79
80    def _get_base_dir(self, start_dir):
81        """ Figure out the base dir (where the top-level cmake file is) """
82        base_dir = os.path.abspath(start_dir)
83        if self._check_directory(base_dir):
84            return base_dir
85        else:
86            (up_dir, this_dir) = os.path.split(base_dir)
87            if os.path.split(up_dir)[1] == 'include':
88                up_dir = os.path.split(up_dir)[0]
89            if self._check_directory(up_dir):
90                return up_dir
91        return None
92
93    def _get_build_dir(self, mod_info):
94        """ Figure out the build dir (i.e. where you run 'cmake'). This checks
95        for a file called CMakeCache.txt, which is created when running cmake.
96        If that hasn't happened, the build dir cannot be detected, unless it's
97        called 'build', which is then assumed to be the build dir. """
98        base_build_dir = mod_info['base_dir']
99        if 'is_component' in list(mod_info.keys()):
100            (base_build_dir, rest_dir) = os.path.split(base_build_dir)
101        has_build_dir = os.path.isdir(os.path.join(base_build_dir, 'build'))
102        if (has_build_dir and os.path.isfile(os.path.join(base_build_dir, 'CMakeCache.txt'))):
103            return os.path.join(base_build_dir, 'build')
104        else:
105            for (dirpath, dirnames, filenames) in os.walk(base_build_dir):
106                if 'CMakeCache.txt' in filenames:
107                    return dirpath
108        if has_build_dir:
109            return os.path.join(base_build_dir, 'build')
110        return None
111
112    def _get_include_dirs(self, mod_info):
113        """ Figure out include dirs for the make process. """
114        inc_dirs = []
115        path_or_internal = {True: 'INTERNAL',
116                            False: 'PATH'}['is_component' in list(mod_info.keys())]
117        try:
118            cmakecache_fid = open(os.path.join(mod_info['build_dir'], 'CMakeCache.txt'))
119            for line in cmakecache_fid:
120                if line.find('GNURADIO_RUNTIME_INCLUDE_DIRS:{}'.format(path_or_internal)) != -1:
121                    inc_dirs += line.replace('GNURADIO_RUNTIME_INCLUDE_DIRS:{}='.format(path_or_internal), '').strip().split(';')
122        except IOError:
123            pass
124        if not inc_dirs and self._suggested_dirs is not None:
125            inc_dirs = [os.path.normpath(path) for path in self._suggested_dirs.split(':') if os.path.isdir(path)]
126        return inc_dirs
127
128    def _pretty_print(elf, mod_info):
129        """ Output the module info in human-readable format """
130        index_names = {'base_dir': 'Base directory',
131                       'modname':  'Module name',
132                       'is_component':  'Is GR component',
133                       'build_dir': 'Build directory',
134                       'incdirs': 'Include directories'}
135        for key in list(mod_info.keys()):
136            if key == 'version':
137                print("        API version: {}".format({
138                        '36': 'pre-3.7',
139                        '37': 'post-3.7',
140                        '38': 'post-3.8',
141                        'autofoo': 'Autotools (pre-3.5)'
142                        }[mod_info['version']]))
143            else:
144                print('%19s: %s' % (index_names[key], mod_info[key]))
145