All submissions to this site are governed by the Second Life Viewer Contribution Agreement. By submitting patches and other information using this site, you acknowledge that you have read, understood, and agreed to those terms.

Review Board 1.6.11

Welcome to the Second Life Viewer Code Review tool.
See the documentation on our wiki for how to use this site.

open-2 (improved progress messages during dependency checks) and open-31 (more standardized short form option switches)

Review Request #150 - Created Feb. 12, 2011 and submitted

Oz Linden Reviewers
viewer
open-2, open-31
None autobuild
I combined open-31 into this because that change is so small and simple that it didn't seem worth a separate commit (besides, while testing the rest of this, I really wanted the short form of --verbose).

open-31 changes the short form of --version to -V, so that the short form of the more commonly used --verbose can be the lower case -v, and adds -n as a short form for --dry-run (as it is used in many versions of make, and in other commands like scp and rsync).

open-2 corrects the problem that (at least in the viewer build) there is a long delay during which there are no progress messages.  The delay is the configure step while packages are checked, including possibly downloading, extracting, and installing them.  Increasing the logging level with the --verbose or --debug switches did not increase the amount of logging during this phase.

The fundamental problem proved to be that the package checks were done in recursively invoked instances of autobuild (I am guessing that the process hierarchy is autobuild->cmake->autobuild, but it could have just been autobuild calling itself directly; this fix should work regardless of the depth of the process tree).  The verbosity control options were not passed through to the child processes, and since there could be some other process in between (as cmake above), it seemed more robust (and relies less on correct configuration) to pass the verbosity control to the children through the environment.

The default verbosity is now read from the AUTOBUILD_LOGGING environment variable, whose contents can be --debug, --verbose, --quiet, or the one letter forms of any of those options (if the environment variable is not set, then the default value is to log warnings and worse, as before).  Any of the options used on the command line override any value from the environment.  Regardless of how the verbosity level is established, the environment variable AUTOBUILD_LOGGING is set for all child processes, so that if any of those (directly or indirectly) are another invocation of autobuild, then that recursive child will use the same verbosity level as the parent (unless, of course the recursive invocation uses an explicit option).  It is true that this also allows the user to set a default verbosity in the shell environment, and that works, but it wasn't really the motivation for the change and I did not add anything to the documentation about it.

With that change made, the options correctly controlled whether or not logging is visible during the package checking phase.  However, the resulting messages had an inconsistent level of detail - some operations (such as uninstall) were very verbose, while others (some of which might take significant time) were logged only at high verbosity levels.  This led to the addition of a few short log messages at the default 'warning' level (which really has the dual meaning "something that might be a problem" and "something that should be seen at the default logging level") in order to make sure that some message is printed before any potentially long operation (downloads, extracts, installs, and uninstalls).  Some other very detailed messages were changed from info to debug levels, as the information they display is really only useful when debugging either a new autobuild configuration or autobuild itself.

I think that the combination of these changes makes the default verbosity reasonably informative (no long silences) without being overwhelming.

(there is a failure displaying the diff for autobuild.configfile.py because it is a one word change of the logging level for a log message added by one of my other changes that has not yet been applied to the trunk)
Manually verified using configuring and building in viewer-autobuild
autobuild/autobuild_main.py
Revision 9ee2db08d677 New Change
... 18 lines hidden [Expand]
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
# THE SOFTWARE.
20
# THE SOFTWARE.
21
# $/LicenseInfo$
21
# $/LicenseInfo$
22

   
22

   
23
import sys
23
import sys
24
import os
24
import os
25
import common
25
import common
26
import argparse
26
import argparse
27
import logging
27
import logging
28

   
28

   

   
29
## Environment variable name used for default log level verbosity

   
30
AUTOBUILD_LOGLEVEL = 'AUTOBUILD_LOGLEVEL'

   
31

   
29
class run_help(argparse.Action):
32
class run_help(argparse.Action):
30
    def __call__(self, parser, namespace, values, option_string=None):
33
    def __call__(self, parser, namespace, values, option_string=None):
31
        parser.parent.search_for_and_import_tools(parser.parent.tools_list)
34
        parser.parent.search_for_and_import_tools(parser.parent.tools_list)
32
        parser.parent.register_tools(parser.parent.tools_list)
35
        parser.parent.register_tools(parser.parent.tools_list)
33
        print parser.format_help()
36
        print parser.format_help()
34
        parser.exit(0)
37
        parser.exit(0)
35

   
38

   
36

   
39

   
37
class Autobuild(object):
40
class Autobuild(object):
38
    def __init__(self):
41
    def __init__(self):
39
        self.parser = argparse.ArgumentParser(
42
        self.parser = argparse.ArgumentParser(
40
            description='Autobuild', prog='autobuild', add_help=False)
43
            description='Autobuild', prog='autobuild', add_help=False)
41
        
44
        
42
        self.parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0')
45
        self.parser.add_argument('-V', '--version', action='version', version='%(prog)s 1.0')
43

   
46

   
44
        self.subparsers = self.parser.add_subparsers(title='Sub Commands',
47
        self.subparsers = self.parser.add_subparsers(title='Sub Commands',
45
            description='Valid Sub Commands',
48
            description='Valid Sub Commands',
46
            help='Sub Command help')
49
            help='Sub Command help')
47

   
50

   
48
    def listdir(self, dir):
51
    def listdir(self, dir):
49
        files = os.listdir(dir)
52
        files = os.listdir(dir)
50
        if 'AutobuildTool_test.py' in files:
53
        if 'AutobuildTool_test.py' in files:
51
            del files[files.index('AutobuildTool_test.py')]
54
            del files[files.index('AutobuildTool_test.py')]
52
        return files
55
        return files
... 26 lines hidden [Expand]
def try_to_import_tool(self, tool, tools_list):
79
        tool_module_name = 'autobuild_tool_' + tool
82
        tool_module_name = 'autobuild_tool_' + tool
80
        tool_file_name = tool_module_name + '.py'
83
        tool_file_name = tool_module_name + '.py'
81
        full_tool_path = os.path.join(autobuild_package_dir, tool_file_name)
84
        full_tool_path = os.path.join(autobuild_package_dir, tool_file_name)
82
        if os.path.exists(full_tool_path):
85
        if os.path.exists(full_tool_path):
83
            possible_tool_module = __import__(tool_module_name, globals(), locals(), []);
86
            possible_tool_module = __import__(tool_module_name, globals(), locals(), []);
84
            if(getattr(possible_tool_module, 'AutobuildTool', None)):
87
            if(getattr(possible_tool_module, 'AutobuildTool', None)):
85
                tools_list.append(possible_tool_module)
88
                tools_list.append(possible_tool_module)
86
                instance = self.register_tool(possible_tool_module)
89
                instance = self.register_tool(possible_tool_module)
87
                return instance
90
                return instance
88
        return -1
91
        return -1

   
92
        

   
93
    def get_default_loglevel_from_environment(self):

   
94
        """

   
95
        Returns a default log level based on the AUTOBUILD_LOGLEVEL environment variable

   
96

   

   
97
        This may be used directly by the user, but in combination with the

   
98
        set_recursive_loglevel method below also ensures 

   
99
        that any recursive invocation of autobuild inherits the same level as the

   
100
        parent, even if intervening commands do not pass it through.

   
101
        """

   
102
        try:

   
103
            environment_level = os.environ[AUTOBUILD_LOGLEVEL]

   
104
        except KeyError:

   
105
            environment_level = ''

   
106

   

   
107
        if environment_level == '--quiet' or environment_level == '-q' :

   
108
            return logging.ERROR

   
109
        elif environment_level == '':

   
110
            return logging.WARNING

   
111
        elif environment_level == '--verbose' or environment_level == '-v' :

   
112
            return logging.INFO

   
113
        elif environment_level == '--debug' or environment_level == '-d' :

   
114
            return logging.DEBUG

   
115
        else:

   
116
            raise AutobuildError("invalid %s value '%s'" % (AUTOBUILD_LOGLEVEL, environment_level))

   
117

   

   
118
    def set_recursive_loglevel(self, logger, level):

   
119
        """

   
120
        Sets the logger level, and also saves the equivalent option argument

   
121
        in the AUTOBUILD_LOGLEVEL environment variable so that any recursive

   
122
        invocation of autobuild uses the same level

   
123
        """

   
124
        logger.setLevel(level)

   
125

   

   
126
        if level == logging.ERROR:

   
127
            os.environ[AUTOBUILD_LOGLEVEL] = '--quiet'

   
128
        elif level == logging.WARNING:

   
129
            os.environ[AUTOBUILD_LOGLEVEL] = ''

   
130
        elif level == logging.INFO:

   
131
            os.environ[AUTOBUILD_LOGLEVEL] = '--verbose'

   
132
        elif level == logging.DEBUG:

   
133
            os.environ[AUTOBUILD_LOGLEVEL] = '--debug'

   
134
        else:

   
135
            raise common.AutobuildError("invalid effective log level %s" % logging.getLevelName(level))
89

   
136

   
90

   
137

   
91
    def main(self, args_in):
138
    def main(self, args_in):
92
    
139
    

   
140
        logger = logging.getLogger('autobuild')

   
141
        logger.addHandler(logging.StreamHandler())

   
142
        default_loglevel = self.get_default_loglevel_from_environment() 

   
143

   
93
        self.tools_list = []
144
        self.tools_list = []
94
        
145
        
95
        self.parser.parent = self
146
        self.parser.parent = self
96
        self.parser.add_argument('--help',
147
        self.parser.add_argument('--help',
97
            help='find all valid Autobuild Tools and show help', action=run_help,
148
            help='find all valid Autobuild Tools and show help', action=run_help,
98
            nargs='?', default=argparse.SUPPRESS)
149
            nargs='?', default=argparse.SUPPRESS)
99
        
150
        
100
        argdefs = (
151
        argdefs = (
101
            (('--dry-run',),
152
            (('-n', '--dry-run',),
102
                dict(help='run tool in dry run mode if available', action='store_true')),
153
                dict(help='run tool in dry run mode if available', action='store_true')),
103
             (('--quiet',),
154

   
155
            ## NOTE: if the mapping of verbosity controls (--{quiet,verbose,debug})

   
156
            ##       is changed here, it must be changed to match in set_recursive_loglevel

   
157
            ##       and get_default_loglevel_from_environment methods above.

   
158
             (('-q', '--quiet',),
104
                dict(help='minimal output', action='store_const',
159
                dict(help='minimal output', action='store_const',
105
                    const=logging.ERROR, dest='logging_level', default=logging.WARNING)),
160
                     const=logging.ERROR, dest='logging_level', default=default_loglevel)),
106
             (('--verbose',),
161
             (('-v', '--verbose',),
107
                dict(help='verbose output', action='store_const', const=logging.INFO, dest='logging_level')),
162
                dict(help='verbose output', action='store_const', const=logging.INFO, dest='logging_level')),
108
             (('--debug',),
163
             (('-d', '--debug',),
109
                dict(help='debug output', action='store_const', const=logging.DEBUG, dest='logging_level')),
164
                dict(help='debug output', action='store_const', const=logging.DEBUG, dest='logging_level')),
110
        )
165
        )
111
        for args, kwds in argdefs:
166
        for args, kwds in argdefs:
112
            self.parser.add_argument(*args, **kwds)
167
            self.parser.add_argument(*args, **kwds)
113
            
168
            
114
        tool_to_run = -1;
169
        tool_to_run = -1;
115

   
170

   
116
        for arg in args_in:
171
        for arg in args_in:
117
            if arg[0] != '-':
172
            if arg[0] != '-':
118
                tool_to_run = self.try_to_import_tool(arg, self.tools_list)
173
                tool_to_run = self.try_to_import_tool(arg, self.tools_list)
119
                if tool_to_run != -1:
174
                if tool_to_run != -1:
120
                    # Define all the global arguments as also being legal
175
                    # Define all the global arguments as also being legal
121
                    # for the subcommand, e.g. support both
176
                    # for the subcommand, e.g. support both
122
                    # autobuild --dry-run upload args... and
177
                    # autobuild --dry-run upload args... and
123
                    # autobuild upload --dry-run args...
178
                    # autobuild upload --dry-run args...
124
                    for args, kwds in argdefs:
179
                    for args, kwds in argdefs:
125
                        self.new_tool_subparser.add_argument(*args, **kwds)
180
                        self.new_tool_subparser.add_argument(*args, **kwds)
126
                break
181
                break
127

   
182

   
128
        args = self.parser.parse_args(args_in)
183
        args = self.parser.parse_args(args_in)
129
                
184
130
        logger = logging.getLogger('autobuild')
185
        self.set_recursive_loglevel(logger, args.logging_level)
131
        logger.setLevel(args.logging_level)
186
132
        logger.addHandler(logging.StreamHandler())

   
133
 

   
134
        if tool_to_run != -1:
187
        if tool_to_run != -1:
135
            tool_to_run.run(args)
188
            tool_to_run.run(args)
136

   
189

   
137
        return 0
190
        return 0
138

   
191

   
139
if __name__ == "__main__":
192
if __name__ == "__main__":
140
    sys.exit( Autobuild().main( sys.argv[1:] ) )
193
    sys.exit( Autobuild().main( sys.argv[1:] ) )
141

   
194

   
142

   
195

   
143

   
196

   
autobuild/autobuild_tool_install.py
Revision 9ee2db08d677 New Change
 
autobuild/autobuild_tool_source_environment.py
Revision 9ee2db08d677 New Change
 
autobuild/common.py
Revision 9ee2db08d677 New Change
 
autobuild/configfile.py
Revision 9ee2db08d677 New Change
 
  1. autobuild/autobuild_main.py: Loading...
  2. autobuild/autobuild_tool_install.py: Loading...
  3. autobuild/autobuild_tool_source_environment.py: Loading...
  4. autobuild/common.py: Loading...
  5. autobuild/configfile.py: Loading...