Exemplo n.º 1
0
    def build(self):
        self._fetch_work_src_tree()
        self._make_canonical_work_src_tree()
        self._clean()
        self._get_version()

        if self.version:
            self.logger.info('C program version is "{0}"'.format(self.version))

        self._configure()

        if self.configuration:
            self.logger.info('C program configuration is "{0}"'.format(
                self.configuration))

        self._build()

        if os.path.isdir(self.target_program_desc['build base']):
            shutil.rmtree(self.target_program_desc['build base'])

        if 'extra Clade options' in self.target_program_desc:
            clade_conf = dict(self._CLADE_CONF)
            clade_conf.update(self.target_program_desc['extra Clade options'])
        else:
            clade_conf = self._CLADE_CONF

        clade = Clade(work_dir=self.target_program_desc['build base'],
                      cmds_file=os.path.join(self.work_src_tree, 'cmds.txt'),
                      conf=clade_conf,
                      preset=self._CLADE_PRESET)
        clade.parse_list(
            ["CrossRef", "Callgraph", "Variables", "Typedefs", "Macros"])

        self.logger.info(
            'Save project attributes, working source trees and target program description to build base'
        )
        clade.add_meta_by_key('project attrs', [{
            'name':
            'project',
            'value': [{
                'name': 'name',
                'value': type(self).__name__
            }, {
                'name': 'architecture',
                'value': self.architecture
            }, {
                'name': 'version',
                'value': self.version
            }, {
                'name': 'configuration',
                'value': self.configuration
            }]
        }])
        clade.add_meta_by_key('working source trees', self.work_src_trees)
        clade.add_meta_by_key('target program description',
                              self.target_program_desc)

        self.logger.info('Remove temporary directories')
        for tmp_dir in self.tmp_dirs:
            shutil.rmtree(tmp_dir)
Exemplo n.º 2
0
def test_add_meta_bad(tmpdir):
    c = Clade(tmpdir)

    with pytest.raises(RuntimeError):
        c.add_meta_by_key("test", None)
Exemplo n.º 3
0
class Program:
    _CLADE_CONF = dict()
    _CLADE_PRESET = "base"

    def __init__(self, logger, target_program_desc):
        self.logger = logger
        self.target_program_desc = target_program_desc

        # Main working source tree where various build and auxiliary actions will be performed.
        self.work_src_tree = self.target_program_desc['source code']

        # Program attributes. We expect that architecture is always specified in the target program description while
        # configuration and version can be either obtained during build somehow or remained unspecified.
        self.architecture = self.target_program_desc['architecture']
        self.configuration = None
        self.version = self.target_program_desc.get('version')

        # Working source trees are directories to be trimmed from file names.
        self.work_src_trees = []
        # Temporary directories that should be removed at the end of work.
        self.tmp_dirs = []

        # Path to the Clade cmds.txt file with intercepted commands
        self.cmds_file = os.path.realpath(os.path.join(self.work_src_tree, 'cmds.txt'))

        # Clade API object
        clade_conf = dict(self._CLADE_CONF)
        clade_conf.update(self.target_program_desc.get('extra Clade options', dict()))
        self.clade = Clade(work_dir=self.target_program_desc['build base'],
                           cmds_file=self.cmds_file,
                           conf=clade_conf,
                           preset=self._CLADE_PRESET)

    def _prepare_work_src_tree(self):
        o = urllib.parse.urlparse(self.work_src_tree)
        if o[0] in ('http', 'https', 'ftp'):
            raise NotImplementedError('Source code is provided in unsupported form of a remote archive')
        elif o[0] == 'git':
            raise NotImplementedError('Source code is provided in unsupported form of a remote Git repository')
        elif o[0]:
            raise ValueError('Source code is provided in unsupported form "{0}"'.format(o[0]))

        if os.path.isfile(self.work_src_tree):
            raise NotImplementedError('Source code is provided in unsupported form of an archive')

        # Local git repository
        if os.path.isdir(os.path.join(self.work_src_tree, '.git')):
            self.logger.debug("Source code is provided in form of a Git repository")
            self.__prepare_git_work_src_tree()

        self.work_src_trees.append(os.path.realpath(self.work_src_tree))

    def __prepare_git_work_src_tree(self):
        if 'git repository version' not in self.target_program_desc:
            return

        checkout = self.target_program_desc['git repository version']
        self.logger.info(f'Checkout Git repository "{checkout}"')

        # Repository lock file may remain from some previous crashed git command
        git_index_lock = os.path.join(self.work_src_tree, '.git', 'index.lock')
        if os.path.isfile(git_index_lock):
            os.remove(git_index_lock)

        # In case of dirty Git working directory checkout may fail so clean up it first.
        execute_cmd(self.logger, 'git', 'clean', '-f', '-d', cwd=self.work_src_tree)
        execute_cmd(self.logger, 'git', 'reset', '--hard', cwd=self.work_src_tree)
        execute_cmd(self.logger, 'git', 'checkout', '-f', checkout, cwd=self.work_src_tree)

        try:
            # Use Git describe to properly identify program version
            stdout = execute_cmd(self.logger, 'git', 'describe', cwd=self.work_src_tree, get_output=True)
            self.version = stdout[0]
        except subprocess.CalledProcessError:
            # Use Git repository version from target program description if Git describe failed
            self.version = checkout

    def _run_clade(self):
        if os.path.isdir(self.target_program_desc['build base']):
            shutil.rmtree(self.target_program_desc['build base'])

        self.clade.parse_list(self.clade.conf['extensions'])

        self.logger.info('Save project attributes, working source trees and target program description to build base')
        attrs = [
            {
                'name': 'name',
                'value': type(self).__name__
            },
            {
                'name': 'architecture',
                'value': self.architecture
            },
            {
                'name': 'version',
                'value': self.version
            }
        ]
        if self.configuration:
            attrs.append({
                'name': 'configuration',
                'value': self.configuration
            })
        self.clade.add_meta_by_key('project attrs', [{
            'name': 'project',
            'value': attrs
        }])
        self.clade.add_meta_by_key('working source trees', self.work_src_trees)
        self.clade.add_meta_by_key('target program description', self.target_program_desc)

    @staticmethod
    def build_wrapper(build):
        '''Wrapper for build() method'''
        def wrapper(self, *args, **kwargs):
            try:
                return build(self, *args, **kwargs)
            finally:
                for tmp_dir in self.tmp_dirs:
                    self.logger.info(f'Remove temporary directory "{tmp_dir}"')
                    shutil.rmtree(tmp_dir)

                if os.path.exists(self.cmds_file):
                    os.remove(self.cmds_file)

        return wrapper

    def build(self):
        ...