summaryrefslogtreecommitdiff
path: root/morph
blob: 84ccefbc1a9dad902b2e77f96455da0776da2b75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#!/usr/bin/python
#
# WARNING: THIS IS HIGHLY EXPERIMENTAL CODE RIGHT NOW. JUST PROOF OF CONCEPT.
# DO NOT RUN UNTIL YOU KNOW WHAT YOU ARE DOING.
# 
# Copyright (C) 2011  Codethink Limited
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


import cliapp
import json
import logging
import os
import shutil
import tempfile

import morphlib


class Morph(cliapp.Application):

    def add_settings(self):
        self.settings.boolean(['verbose', 'v'], 'show what is happening')
        self.settings.string(['git-base-url'],
                             'prepend URL to git repos that are not URLs',
                             metavar='URL')
        self.settings.string(['cachedir'], 
                             'put build results in DIR (default: %default)',
                             metavar='DIR', default='.')
        self.settings.boolean(['no-ccache'], 'do not use ccache')
        self.settings.boolean(['no-distcc'], 'do not use distcc')
        self.settings.integer(['max-jobs'], 
                              'run at most N parallel jobs with make (default '
                                'is to a value based on the number of CPUs '
                                'in the machine running morph',
                              metavar='N',
                              default=0)
        self.settings.boolean(['keep-path'], 
                              'do not touch the PATH environment variable '
                                '(use with tests ONLY)')
        self.settings.boolean(['bootstrap'], 
                              'build stuff in bootstrap mode; this is '
                                'DANGEROUS and will install stuff on your '
                                'system')

        self.settings.boolean(['test-console'], 
                              'show what the system outputs on the serial '
                                    'console during tests')
        self.settings.integer(['test-timeout'], 
                              'abort test if system doesn\'t produce '
                                'expected output in TIMEOUT seconds '
                                '(default: %default)',
                              metavar='TIMEOUT',
                              default=10)

    def cmd_build(self, args):
        '''Build a binary from a morphology.
        
        Command line arguments are the repository, git tree-ish reference,
        and morphology filename. The binary gets put into the cache.
        
        (The triplet of command line arguments may be repeated as many
        times as necessary.)
        
        '''

        tempdir = morphlib.tempdir.Tempdir()
        builder = morphlib.builder.Builder(tempdir, self)

        if not os.path.exists(self.settings['cachedir']) and os.getuid() != 0:
            os.mkdir(self.settings['cachedir'])

        while len(args) >= 3:
            repo, ref, filename = args[:3]
            args = args[3:]
            self.msg('Building %s - %s - %s' % (repo, ref, filename))
            builder.build(repo, ref, filename)
        
        tempdir.remove()

        if args:
            raise cliapp.AppException('Extra args on command line: %s' % args)
            
    def cmd_testsysimg(self, args):
        '''Run tests for a built system image.
        
        Command line arguments are the filename of the system image,
        and the filenames of the Python modules that contain the test
        "stories". Each module must have a variable called "story",
        which is a list of tuples. Each tuple is either two strings
        (one to send, the other a regular expression for what is expected
        in return), or two strings and a timeout in seconds.
        
        testsysimg runs the image under KVM, and accesses it via a
        serial console, and runs the test stories, one by one.
        
        '''

        if not args:
            raise cliapp.AppException('Missing command line arguments. '
                                      'Run with --help to see usage.')

        system = morphlib.tester.KvmSystem(args[0], 
                                        verbose=self.settings['test-console'],
                                        timeout=self.settings['test-timeout'])

        for filename in args[1:]:
            self.msg('Running %s' % filename)
            module = morphlib.tester.load_module(filename)
            story_steps = getattr(module, 'story')
            story = morphlib.tester.TestStory(system, story_steps, self.msg)
            story.run()
            self.msg('Finished OK.')

    def msg(self, msg):
        '''Show a message to the user about what is going on.'''
        logging.debug(msg)
        if self.settings['verbose']:
            self.output.write('%s\n' % msg)
            self.output.flush()


if __name__ == '__main__':
    Morph().run()