示例#1
0
 def get_default_build_configurations(self, platform_name=None):
     """
     Returns the platform specific build configurations which are marked as default.
     """
     if platform_name is None:
         common.get_current_platform()
     return [
         value for (key, value) in self.get_platform(
             platform_name).configurations.iteritems() if value.default
     ]
def _build_a_configuration(config,
                           build_configuration,
                           platform_name=common.get_current_platform(),
                           extra_arguments=[],
                           dry_run=False,
                           environment={}):
    try:
        common_build_configuration = \
            config.get_build_configuration(build_configuration.name, platform_name=common.PLATFORM_COMMON)
        parent_build = common_build_configuration.build
    except Exception as e:
        if logger.getEffectiveLevel() <= logging.DEBUG:
            logger.exception(e)
        logger.debug('no common platform found')
        parent_build = None
    if build_configuration.build is not None:
        build_executable = copy.copy(build_configuration.build)
        build_executable.parent = parent_build
    elif parent_build is not None:
        logger.info('no build executable defined; falling back to parent')
        build_executable = parent_build
    else:
        logger.info('no build executable defined; doing nothing')
        return 0
    logger.info('executing build command:\n  %s',
                build_executable.__str__(extra_arguments))
    if not dry_run:
        return build_executable(extra_arguments, environment=environment)
    else:
        return 0
    def run(self, args):
        logger.debug("loading " + args.autobuild_filename)
        platform=common.get_current_platform()
        if args.clean_only:
            logger.info("packaging with --clean-only required")
        if args.check_license:
            logger.warning("The --skip-license-check option is deprecated; it now has no effect")
        if args.results_file and os.path.exists(args.results_file):
            if args.dry_run:
                logger.info("would have removed previous results: %s" % args.results_file)
            else:
                logger.debug("clearing previous results: %s" % args.results_file)
                os.remove(args.results_file)
        config = configfile.ConfigurationDescription(args.autobuild_filename)

        build_dirs = common.select_directories(args, config, "build", "packaging",
                                               lambda cnf:
                                               config.get_build_directory(cnf, platform))

        if not build_dirs:
            build_dirs = [config.get_build_directory(None, platform)]
        is_clean = True
        for build_dir in build_dirs:
            package(config, build_dir, platform, archive_filename=args.archive_filename,
                    archive_format=args.archive_format, clean_only=args.clean_only, results_file=args.results_file, dry_run=args.dry_run)
示例#4
0
 def get_all_build_configurations(self, platform_name=None):
     """
     Returns all build configurations for the platform.
     """
     if platform_name is None:
         platform_name = common.get_current_platform()
     return self.get_platform(platform_name).configurations.values()
示例#5
0
    def get_build_directory(self, configuration, platform_name=None):
        """
        Returns the absolute path to the build directory for the platform.
        """
        build_directory = None
        if platform_name is None:
            platform_name = common.get_current_platform()
        platform_description = self.get_platform(platform_name)
        common_platform_description = self.package_description.platforms.get(
            common.PLATFORM_COMMON, None)
        config_directory = os.path.dirname(self.path)
        # Try specific configuration build_directory first.
        if hasattr(configuration, 'build_directory'
                   ) and configuration.build_directory is not None:
            build_directory = configuration.build_directory
            if not os.path.isabs(build_directory):
                build_directory = os.path.abspath(
                    os.path.join(config_directory, build_directory))
        elif platform_description.build_directory is not None:
            build_directory = platform_description.build_directory
            if not os.path.isabs(build_directory):
                build_directory = os.path.abspath(
                    os.path.join(config_directory, build_directory))
        elif common_platform_description is not None and common_platform_description.build_directory is not None:
            build_directory = common_platform_description.build_directory
            if not os.path.isabs(build_directory):
                build_directory = os.path.abspath(
                    os.path.join(config_directory, build_directory))
        else:
            build_directory = config_directory

        common.establish_build_dir(build_directory)  # save global state
        return build_directory
示例#6
0
 def make_build_directory(self):
     """
     Makes the working platform's build directory if it does not exist and returns a path to it.
     """
     build_directory = self.get_build_directory(common.get_current_platform())
     if not os.path.isdir(build_directory):
         os.makedirs(build_directory)
     return build_directory
示例#7
0
 def delete(self, platform=get_current_platform(), **kwargs):
     """
     Delete the named config value.
     """
     print "Deleting %s archive entry." % platform
     platform_description = self.config.get_platform(platform)
     if platform_description is not None:
         platform_description.archive = None
 def run(self, platform=get_current_platform(), name=CONFIG_NAME_DEFAULT, 
               command=DEFAULT_CONFIG_CMD, options='', arguments='', default=None):
     """
     Updates the configure command.
     """
     new_command = { 'command':command, 
                     'options':listify_str(options), 
                     'arguments':listify_str(arguments)}
     build_config_desc = self.create_or_update_build_config_desc(name, platform, default=default, configure=new_command)
示例#9
0
 def run(self,
         platform=get_current_platform(),
         format=None,
         hash_algorithm=None):
     """
     Configure platform archive details.
     """
     self._create_or_update_platform_archive(platform, format,
                                             hash_algorithm)
示例#10
0
 def get_default_build_configurations(self, platform_name=get_current_platform()):
     """
     Returns the platform specific build configurations which are marked as default.
     """
     default_build_configurations = []
     for (key, value) in self.get_platform(platform_name).configurations.iteritems():
         if value.default:
             default_build_configurations.append(value)
     return default_build_configurations
def do_source_environment(args):
    var_mapping = {
            'AUTOBUILD_EXECUTABLE_PATH':common.get_autobuild_executable_path(),
            'AUTOBUILD_VERSION_STRING':common.AUTOBUILD_VERSION_STRING,
            'AUTOBUILD_PLATFORM':common.get_current_platform(),
            'MAKEFLAGS':"",
            'DISTCC_HOSTS':"",
        }

    if common.get_current_platform() is "windows":
        try:
            # reset stdout in binary mode so sh doesn't get confused by '\r'
            import msvcrt
            msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
        except ImportError:
            # cygwin gets a pass
            pass

        # load vsvars32.bat variables
        # *TODO - find a way to configure this instead of hardcoding to vc80
        try:
            vs_ver = os.environ['AUTOBUILD_VSVER']
        except KeyError:
            vs_ver = "80"
            
        var_mapping.update(load_vsvars(vs_ver))
        var_mapping.update(AUTOBUILD_EXECUTABLE_PATH=("$(cygpath -u '%s')" % common.get_autobuild_executable_path()))

        try:
            use_ib = int(os.environ['USE_INCREDIBUILD'])
        except ValueError:
            logger.warning("USE_INCREDIBUILD environment variable contained garbage %r (expected 0 or 1), turning incredibuild off" % os.environ['USE_INCREDIBUILD'])
            use_ib = 0
        except KeyError:
            use_ib = int(bool(common.find_executable('BuildConsole')))

        var_mapping.update(USE_INCREDIBUILD=use_ib)

    sys.stdout.write(environment_template % var_mapping)

    if get_params:
        # *TODO - run get_params.generate_bash_script()
        pass
示例#12
0
 def get_build_configuration(self, build_configuration_name, platform_name=get_current_platform()):
     """
     Returns the named build configuration for the platform. 
     """
     build_configuration = \
         self.get_platform(platform_name).configurations.get(build_configuration_name, None)
     if build_configuration is not None:
         return build_configuration
     else:
         raise ConfigurationError("no configuration for build configuration '%s' found; one may be created using 'autobuild edit build'" % 
             build_configuration_name)
 def register(self, parser):
     parser.description = "specify manifest of artifacts to be packaged by the 'autobuild package' command."
     parser.add_argument('--config-file',
         dest='config_file',
         default=configfile.AUTOBUILD_CONFIG_FILE,
         help='(defaults to $AUTOBUILD_CONFIG_FILE or "autobuild.xml")')
     parser.add_argument('--platform','-p', default=get_current_platform(),
         help="the platform manifest to manipulate")
     parser.add_argument('command', nargs='?', default='print',
         help="manifest command: add, remove, clear, or print")
     parser.add_argument('pattern', nargs='*', help='a file pattern')
    def run(self, args):
        platform = common.get_current_platform()
        common.establish_build_id(
            args.build_id)  # sets id (even if not specified),
        # and stores in the AUTOBUILD_BUILD_ID environment variable
        config = configfile.ConfigurationDescription(args.config_file)
        package_errors = configfile.check_package_attributes(config)
        if package_errors:
            raise ConfigurationError("%s\n    in configuration %s" \
                                     % (package_errors, args.config_file))
        current_directory = os.getcwd()
        try:
            build_configurations = common.select_configurations(
                args, config, "configuring for")
            if not build_configurations:
                logger.error(
                    "no applicable configurations found.\n"
                    "did you remember to mark a configuration as default?\n"
                    "autobuild cowardly refuses to do nothing!")

            for build_configuration in build_configurations:
                # Get enriched environment based on the current configuration
                environment = get_enriched_environment(
                    build_configuration.name)
                # then get a copy of the config specific to this build
                # configuration
                bconfig = config.copy()
                # and expand its $variables according to the environment.
                bconfig.expand_platform_vars(environment)
                # Re-fetch the build configuration so we have its expansions.
                build_configuration = bconfig.get_build_configuration(
                    build_configuration.name, platform_name=platform)
                build_directory = bconfig.make_build_directory(
                    build_configuration,
                    platform=platform,
                    dry_run=args.dry_run)
                if not args.dry_run:
                    logger.debug("configuring in %s" % build_directory)
                    os.chdir(build_directory)
                else:
                    logger.info("configuring in %s" % build_directory)
                result = _configure_a_configuration(bconfig,
                                                    build_configuration,
                                                    args.additional_options,
                                                    args.dry_run,
                                                    environment=environment)
                if result != 0:
                    raise ConfigurationError(
                        "default configuration returned %d" % result)
        finally:
            os.chdir(current_directory)
    def run(self, args):
        platform = common.get_current_platform()
        logger.debug("uninstalling for platform " + platform)

        installed_filename = args.installed_filename
        if os.path.isabs(installed_filename):
            installed_filenames = [installed_filename]
        else:
            # This logic handles the (usual) case when installed_filename is
            # relative to install_dir. Therefore we must figure out install_dir.

            # write packages into 'packages' subdir of build directory by default
            config = configfile.ConfigurationDescription(args.install_filename)
            # establish a build directory so that the install directory is relative to it
            build_configurations = common.select_configurations(
                args, config, "uninstalling for")
            if not build_configurations:
                logger.error(
                    "no applicable configurations found.\n"
                    "did you remember to mark a configuration as default?\n"
                    "autobuild cowardly refuses to do nothing!")

            for build_configuration in build_configurations:
                # Get enriched environment based on the current configuration
                environment = get_enriched_environment(
                    build_configuration.name)
                # then get a copy of the config specific to this build
                # configuration
                bconfig = config.copy()
                # and expand its $variables according to the environment.
                bconfig.expand_platform_vars(environment)
                # Re-fetch the build configuration so we have its expansions.
                build_configuration = bconfig.get_build_configuration(
                    build_configuration.name, platform_name=platform)
                build_directory = bconfig.get_build_directory(
                    build_configuration, platform_name=platform)
                logger.debug("build directory: %s" % build_directory)
                installed_filenames = \
                  [os.path.realpath(os.path.join(install_dir, installed_filename))
                   for install_dir in
                   common.select_directories(args, config,
                                            "install", "uninstalling",
                                            lambda cnf:
                                            os.path.join(build_directory,
                                                         "packages"))]

        logger.debug("installed filenames: %s" % installed_filenames)
        for installed_filename in installed_filenames:
            uninstall_packages(args, installed_filename, args.package,
                               args.dry_run)
示例#16
0
 def run(self, args):
     platform = common.get_current_platform()
     config = configfile.ConfigurationDescription(args.config_file)
     if args.command == 'add':
         [add(config, platform, p) for p in args.pattern]
     elif args.command == 'remove':
         [remove(config, platform, p) for p in args.pattern]
     elif args.command == 'clear':
         clear(config, platform)
     elif args.command == 'print':
         print_manifest(config, platform)
     else:
         raise ManifestError('unknown command %s' % args.command)
     if not args.dry_run:
         config.save()
示例#17
0
 def get_build_configuration(self,
                             build_configuration_name,
                             platform_name=None):
     """
     Returns the named build configuration for the platform.
     """
     if platform_name is None:
         platform_name = common.get_current_platform()
     try:
         return self.get_platform(
             platform_name).configurations[build_configuration_name]
     except KeyError:
         raise ConfigurationError(
             "no configuration for build configuration '%s' found; "
             "one may be created using 'autobuild edit build'" %
             build_configuration_name)
示例#18
0
    def run(self, args):
        UTF8Writer = codecs.getwriter('utf8')
        sys.stdout = UTF8Writer(sys.stdout)

        platform = common.get_current_platform()
        logger.debug("installing platform " + platform)

        # load the list of packages to install
        logger.debug("loading " + args.install_filename)
        config = configfile.ConfigurationDescription(args.install_filename)

        # establish a build directory so that the install directory is relative to it
        build_configurations = common.select_configurations(
            args, config, "installing for")
        if not build_configurations:
            logger.error(
                "no applicable configurations found.\n"
                "did you remember to mark a configuration as default?\n"
                "autobuild cowardly refuses to do nothing!")

        for build_configuration in build_configurations:
            # Get enriched environment based on the current configuration
            environment = get_enriched_environment(build_configuration.name)
            # then get a copy of the config specific to this build
            # configuration
            bconfig = config.copy()
            # and expand its $variables according to the environment.
            bconfig.expand_platform_vars(environment)
            # Re-fetch the build configuration so we have its expansions.
            build_configuration = bconfig.get_build_configuration(
                build_configuration.name, platform_name=platform)
            build_directory = bconfig.get_build_directory(
                build_configuration, platform_name=platform)

            # write packages into 'packages' subdir of build directory
            install_dirs = \
                common.select_directories(args, bconfig,
                                          "install", "installing packages for",
                                          lambda cnf:
                                          os.path.join(bconfig.make_build_directory(cnf, platform=platform, dry_run=args.dry_run),
                                                       "packages"))

            # get the absolute paths to the install dir and installed-packages.xml file
            for install_dir in install_dirs:
                install_dir = os.path.realpath(install_dir)
                install_packages(args, bconfig, install_dir, platform,
                                 args.package)
示例#19
0
 def run(self,
         platform=get_current_platform(),
         name=CONFIG_NAME_DEFAULT,
         command=DEFAULT_CONFIG_CMD,
         options='',
         arguments='',
         default=None):
     """
     Updates the configure command.
     """
     new_command = {
         'command': command,
         'options': listify_str(options),
         'arguments': listify_str(arguments)
     }
     build_config_desc = self.create_or_update_build_config_desc(
         name, platform, default=default, configure=new_command)
示例#20
0
 def get_build_directory(self, platform_name=get_current_platform()):
     """
     Returns the absolute path to the build directory for the platform.
     """
     platform_description = self.get_platform(platform_name)
     common_platform_description = self.package_description.platforms.get('common', None)
     config_directory = os.path.dirname(self.path)
     if platform_description.build_directory is not None:
         build_directory = platform_description.build_directory
         if not os.path.isabs(build_directory):
             build_directory = os.path.abspath(os.path.join(config_directory, build_directory))
     elif common_platform_description is not None and common_platform_description.build_directory is not None:
         build_directory = common_platform_description.build_directory
         if not os.path.isabs(build_directory):
             build_directory = os.path.abspath(os.path.join(config_directory, build_directory))
     else:
         build_directory = config_directory
     return build_directory
示例#21
0
 def make_build_directory(self,
                          configuration,
                          platform=common.get_current_platform(),
                          dry_run=False):
     """
     Makes the working platform's build directory if it does not exist and returns a path to it.
     """
     logger.debug("make_build_directory platform %s" % platform)
     build_directory = self.get_build_directory(configuration,
                                                platform_name=platform)
     if not os.path.isdir(build_directory):
         if not dry_run:
             logger.info("Creating build directory %s" % build_directory)
             os.makedirs(build_directory)
         else:
             logger.warn("Dry run mode: not creating build directory %s" %
                         build_directory)
     return build_directory
 def register(self, parser):
     parser.description = "package the artifacts produced by the 'autobuild build' command into a package archive for distribution."
     parser.add_argument(
         '--config-file',
         default=configfile.AUTOBUILD_CONFIG_FILE,
         dest='autobuild_filename',
         help="the file used to describe how to build the package\n  (defaults to $AUTOBUILD_CONFIG_FILE or \"autobuild.xml\")")
     parser.add_argument(
         '--archive-name',
         default=None,
         dest='archive_filename',
         help='the filename of the archive that autobuild will create')
     parser.add_argument(
         '-p', '--platform', 
         default=common.get_current_platform(),
         dest='platform',
         help='override the working platform')
     parser.add_argument(
         '--skip-license-check', 
         action='store_false',
         default=True,
         dest='check_license',
         help="do not perform the license check")
                dylib="$(readlink "$dylib")"
            fi
            install_name_tool -id "@executable_path/../Resources/$dylib" "$dylib"
            if [ "$dylib" != "$dylink" ]; then
                ln -svf "$dylib" "$dylink"
            fi
        fi
    }

    MAKEFLAGS="%(MAKEFLAGS)s"
    #DISTCC_HOSTS="%(DISTCC_HOSTS)s"

    $restore_xtrace
"""

if common.get_current_platform() is "windows":
    windows_template = """
    # disable verbose debugging output
    set +o xtrace

    USE_INCREDIBUILD=%(USE_INCREDIBUILD)d
    build_vcproj() {
        local vcproj=$1
        local config=$2

        if ((%(USE_INCREDIBUILD)d)) ; then
            BuildConsole "$vcproj" /CFG="$config"
        else
            devenv "$vcproj" /build "$config"
        fi
    }
def add_arguments(parser):
    parser.description = "install artifacts of dependency packages for use during the build of the current package"
    parser.add_argument(
        'package',
        nargs='*',
        help='List of packages to consider for installation.')
    parser.add_argument(
        '--config-file',
        default=configfile.AUTOBUILD_CONFIG_FILE,
        dest='install_filename',
        help="The file used to describe what should be installed\n  (defaults to $AUTOBUILD_CONFIG_FILE or \"autobuild.xml\").")
    parser.add_argument(
        '--installed-manifest',
        default=configfile.INSTALLED_CONFIG_FILE,
        dest='installed_filename',
        help='The file used to record what is installed.')
    parser.add_argument(
        '-p', '--platform',
        default=common.get_current_platform(),
        dest='platform',
        help='Override the automatically determined platform.')
    parser.add_argument(
        '--install-dir',
        default=None,
        dest='install_dir',
        help='Where to unpack the installed files.')
    parser.add_argument(
        '--list',
        action='store_true',
        default=False,
        dest='list_archives',
        help="List the archives specified in the package file.")
    parser.add_argument(
        '--list-installed',
        action='store_true',
        default=False,
        dest='list_installed',
        help="List the installed package names and exit.")
    parser.add_argument(
        '--skip-license-check',
        action='store_false',
        default=True,
        dest='check_license',
        help="Do not perform the license check.")
    parser.add_argument(
        '--list-licenses',
        action='store_true',
        default=False,
        dest='list_licenses',
        help="List known licenses and exit.")
    parser.add_argument(
        '--export-manifest',
        action='store_true',
        default=False,
        dest='export_manifest',
        help="Print the install manifest to stdout and exit.")
    parser.add_argument(
        '--as-source',
        action='append',
        dest='as_source',
        default=[],
        help="Get the source for this package instead of prebuilt binary.")
示例#25
0
 def get_all_build_configurations(self, platform_name=get_current_platform()):
     """
     Returns all build configurations for the platform.
     """
     return self.get_platform(platform_name).configurations.values()
示例#26
0
 def get_working_platform(self):
     """
     Returns the working platform description.
     """
     return self.get_platform(common.get_current_platform())
示例#27
0
 def get_working_platform(self):
     """
     Returns the working platform description.
     """
     return self.get_platform(get_current_platform())
def internal_source_environment(configurations, varsfile):
    """
    configurations is a list of requested configurations (e.g. 'Release'). If
    the list isn't empty, the first entry will be used; any additional entries
    will be ignored with a warning.

    varsfile, if not None, is the name of a local variables file as in
    https://bitbucket.org/lindenlab/build-variables/src/tip/variables.

    os.environ['AUTOBUILD_VSVER'] indirectly indicates a Visual Studio
    vcvarsall.bat script from which to load variables. Its values are e.g.
    '100' for Visual Studio 2010 (VS 10), '120' for Visual Studio 2013 (VS 12)
    and so on. A correct value nnn for the running system will identify a
    corresponding VSnnnCOMNTOOLS environment variable.

    Returns a triple of dicts (exports, vars, vsvars):

    exports is intended to propagate down to child processes, hence should be
    exported by the consuming bash shell.

    vars is intended for use by the consuming bash shell, hence need not be
    exported.

    vsvars contains variables set by the relevant Visual Studio vcvarsall.bat
    script. It is an empty dict on any platform but Windows.
    """
    if not common.is_system_windows():
        vsver = None  # N/A
    else:
        try:
            vsver = os.environ['AUTOBUILD_VSVER']
        except KeyError:
            # try to figure out most recent Visual Studio version
            try:
                vsver = _available_vsvers()[-1]
            except IndexError:
                logger.warning(
                    "No Visual Studio install detected -- "
                    "certain configuration variables will not be available")
                vsver = None

    # OPEN-259: it turns out to be important that if AUTOBUILD is already set
    # in the environment, we should LEAVE IT ALONE. So if it exists, use the
    # existing value. Otherwise just use our own executable path.
    autobuild_path = common.get_autobuild_executable_path()
    AUTOBUILD = os.environ.get("AUTOBUILD", autobuild_path)
    # The cross-platform environment_template contains a generic 'vars' slot
    # where we can insert lines defining environment variables. Putting a
    # variable definition into this 'exports' dict causes it to be listed
    # there with an 'export' statement; putting a variable definition into the
    # 'vars' dict lists it there as local to that bash process. Logic just
    # before expanding environment_template populates 'exports' and 'vars'
    # into var_mapping["vars"]. We defer it that long so that conditional
    # logic below can, if desired, add to either 'exports' or 'vars' first.
    exports = dict(
        AUTOBUILD=AUTOBUILD,
        AUTOBUILD_VERSION_STRING=common.AUTOBUILD_VERSION_STRING,
        AUTOBUILD_PLATFORM=common.get_current_platform(),
    )
    vars = dict(
        ##      MAKEFLAGS="",
        ##      DISTCC_HOSTS="",
    )
    vsvars = {}

    # varsfile could have been set either of two ways above, check again
    if varsfile is not None:
        # Read variable definitions from varsfile. Syntax restrictions are
        # documented in the build-variables/variables file itself, but
        # essentially it's the common subset of bash and string.Template
        # expansion functionality.
        # This is what we expect every substantive line in the input file to
        # look like: a valid variable name (starting with letter or
        # underscore, containing only letters, underscores or digits) = a
        # double-quoted value. We do not presently tolerate extra whitespace.
        assign_line = re.compile(r'^([A-Za-z_][A-Za-z0-9_]+)="(.*)"$')
        vfvars = {}
        try:
            with open(varsfile) as vf:
                for linen0, line in enumerate(vf):
                    # skip empty lines and comment lines
                    if line == '\n' or line.startswith('#'):
                        continue
                    match = assign_line.match(line.rstrip())
                    if not match:
                        # Fatal error is the only sure way to get a developer
                        # to fix a bad assignment in the variables file. If we
                        # just skip it with a warning, it could be weeks
                        # before we figure out why some large subset of
                        # third-party packages was built without essential
                        # compiler switches.
                        raise SourceEnvError(
                            "%s(%s): malformed variable assignment:\n%s" %
                            (varsfile, linen0 + 1, line.rstrip()))
                    var, value = match.group(1, 2)
                    try:
                        # Rely on the similarity between string.Template
                        # subtitution syntax and bash substitution syntax.
                        vfvars[var] = string.Template(value).substitute(vfvars)
                    except ValueError as err:
                        raise SourceEnvError(
                            "%s(%s): bad substitution syntax: %s\n%s" %
                            (varsfile, linen0 + 1, err, line.rstrip()))
                    except KeyError as err:
                        raise SourceEnvError(
                            "%s(%s): undefined variable %s:\n%s" %
                            (varsfile, linen0 + 1, err, line.rstrip()))
        except (IOError, OSError) as err:
            # Even though it's only a warning to fail to specify varsfile,
            # it's a fatal error to specify one that doesn't exist or can't be
            # read.
            raise SourceEnvError("%s: can't read '%s': %s" %
                                 (err.__class__.__name__, varsfile, err))

        # Here vfvars contains all the variables set in varsfile. Before
        # passing them along to the 'vars' dict, make a convenience pass over
        # them to extract simpler variable names specific to the platform and
        # build type.

        # If we recognize the current platform, provide shorthand vars for it.
        try:
            # Base this on sys.platform rather than
            # common.get_current_platform() because we don't want to have to
            # enumerate common.PLATFORM_WINDOWS, common.PLATFORM_WINDOWS64,
            # etc. just to blur the distinction between them again.
            platform = dict(
                win32="WINDOWS",
                cygwin="WINDOWS",
                darwin="DARWIN",
                linux2="LINUX",
            )[sys.platform]
        except KeyError:
            logger.warning("Unsupported platform %s: no short names provided" %
                           sys.platform)
        else:
            platform_re = re.compile(r'(.*_BUILD)_%s(.*)$' % platform)
            # use items() rather than iteritems(): we're modifying as we iterate
            for var, value in vfvars.items():
                match = platform_re.match(var)
                if match:
                    # add a shorthand variable that excludes _PLATFORM
                    vfvars[''.join(match.group(1, 2))] = value

        # If caller specified configuration, provide shorthand vars for it.
        # If nothing was specified, configurations will be empty; if something
        # was, take only the first specified configuration.
        if configurations:
            configuration = configurations[0].upper()
            if configurations[1:]:
                logger.warning("Ignoring extra configurations %s" %
                               ", ".join(configurations[1:]))
            configuration_re = re.compile(r'(.*_BUILD)_%s(.*)$' %
                                          configuration)
            # use items() because we're modifying as we iterate
            for var, value in vfvars.items():
                match = configuration_re.match(var)
                if match:
                    # add a shorthand variable that excludes _CONFIGURATION
                    vfvars[''.join(match.group(1, 2))] = value

        # We've been keeping varsfile variables separate so we can make the
        # above convenience passes through them without accidentally matching
        # pre-existing entries in 'vars'. Now dump everything into 'vars'.
        vars.update(vfvars)

    # Let KeyError, if any, propagate: lack of AUTOBUILD_ADDRSIZE would be
    # an autobuild coding error. So would any value for that variable
    # other than what's stated below.
    exports["AUTOBUILD_CONFIGURE_ARCH"] = {
        '32': 'i386',
        '64': 'x86_64',
    }[os.environ["AUTOBUILD_ADDRSIZE"]]

    if common.is_system_windows():
        try:
            use_ib = int(os.environ['USE_INCREDIBUILD'])
        except ValueError:
            logger.warning(
                "USE_INCREDIBUILD environment variable contained garbage %r "
                "(expected 0 or 1)" % os.environ['USE_INCREDIBUILD'])
            use_ib = 0
        except KeyError:
            # We no longer require Incredibuild for Windows builds. Therefore,
            # if you want to engage Incredibuild, you must explicitly set
            # USE_INCREDIBUILD=1. We no longer implicitly set that if
            # BuildConsole.exe is on the PATH.
            use_ib = 0

        vars["USE_INCREDIBUILD"] = str(use_ib)

        # Let KeyError, if any, propagate: lack of AUTOBUILD_ADDRSIZE would be
        # an autobuild coding error. So would any value for that variable
        # other than what's stated below.
        exports["AUTOBUILD_WIN_VSPLATFORM"] = {
            '32': 'Win32',
            '64': 'x64',
        }[os.environ["AUTOBUILD_ADDRSIZE"]]

        if vsver:
            # When one of our build-cmd.sh scripts invokes CMake on Windows, it's
            # probably prudent to use a -G switch for the specific Visual Studio
            # version we want to target. It's not that uncommon for a Windows
            # build host to have multiple VS versions installed, and it can
            # sometimes take a while for us to switch to the newest release. Yet
            # we do NOT want to hard-code the version-specific CMake generator
            # name into each 3p source repo: we know from experience that
            # sprinkling version specificity throughout a large collection of 3p
            # repos is part of what makes it so hard to upgrade the compiler. The
            # problem is that the mapping from vsver to (e.g.) "Visual Studio 12"
            # isn't necessarily straightforward -- we may have to maintain a
            # lookup dict. That dict should not be replicated into each 3p repo,
            # it should be central. It should be here.
            try:
                AUTOBUILD_WIN_CMAKE_GEN = {
                    '120': "Visual Studio 12",
                    '140': "Visual Studio 14",
                    '150': "Visual Studio 15",
                }[vsver]
            except KeyError:
                # We don't have a specific mapping for this value of vsver. Take
                # a wild guess. If we guess wrong, CMake will complain, and the
                # user will have to update autobuild -- which is no worse than
                # what s/he'd have to do anyway if we immediately produced an
                # error here. Plus this way, we defer the error until we hit a
                # build that actually consumes AUTOBUILD_WIN_CMAKE_GEN.
                AUTOBUILD_WIN_CMAKE_GEN = "Visual Studio %s" % (vsver[:-1])
            # Of course CMake also needs to know bit width :-P
            if os.environ["AUTOBUILD_ADDRSIZE"] == "64":
                AUTOBUILD_WIN_CMAKE_GEN += " Win64"
            exports["AUTOBUILD_WIN_CMAKE_GEN"] = AUTOBUILD_WIN_CMAKE_GEN

            # load vsvars32.bat variables
            vsvars = load_vsvars(vsver)

            # Resetting our PROMPT is a bit heavy-handed. Plus the substitution
            # syntax probably differs.
            vsvars.pop("PROMPT", None)

    return exports, vars, vsvars
    def run(self, args):
        platform = common.get_current_platform()
        build_id = common.establish_build_id(
            args.build_id)  # sets id (even if not specified),
        # and stores in the AUTOBUILD_BUILD_ID environment variable
        config = configfile.ConfigurationDescription(args.config_file)
        package_errors = \
            configfile.check_package_attributes(config,
                                                additional_requirements=['version_file'])
        if package_errors:
            # Now that we've deprecated hard-coded version and started
            # requiring version_file instead, provide an explanation when it's
            # missing, instead of confounding a longtime autobuild user with
            # failure to meet a brand-new requirement.
            # Recall that package_errors isa str that also has an attrs
            # attribute. Only emit the verbose message if version_file is
            # actually one of the problematic attributes, and the config file
            # had to be converted from an earlier file format, and the
            # original file format version predates version_file.
            # (missing orig_ver attribute means a current autobuild.xml, which
            # is why we pass get() a default value that bypasses verbose)
            # (version_file was introduced at AUTOBUILD_CONFIG_VERSION 1.3)
            if "version_file" in package_errors.attrs \
            and common.get_version_tuple(config.get("orig_ver", "1.3")) < (1, 3):
                verbose = """
New requirement: instead of stating a particular version number in the %(xml)s
file, we now require you to configure a version_file attribute. This should be
the path (relative to the build_directory) of a small text file containing
only the package version string. Freezing the version number into %(xml)s
means we often forget to update it there. Reading the version number from a
separate text file allows your build script to create that file from data
available in the package. version_file need not be in the manifest; it's used
only by 'autobuild build' to create package metadata.
""" % dict(xml=configfile.AUTOBUILD_CONFIG_FILE)
            else:
                verbose = ""
            # Now, regardless of the value of 'verbose', show the message.
            raise BuildError(''.join(
                (package_errors, "\n    in configuration ", args.config_file,
                 verbose)))
        current_directory = os.getcwd()
        if args.clean_only:
            logger.info("building with --clean-only required")
        try:
            configure_first = not args.do_not_configure
            build_configurations = common.select_configurations(
                args, config, "building for")
            if not build_configurations:
                logger.error(
                    "no applicable configurations found.\n"
                    "did you remember to mark a configuration as default?\n"
                    "autobuild cowardly refuses to do nothing!")

            for build_configuration in build_configurations:
                # Get enriched environment based on the current configuration
                environment = get_enriched_environment(
                    build_configuration.name)
                # then get a copy of the config specific to this build
                # configuration
                bconfig = config.copy()
                # and expand its $variables according to the environment.
                bconfig.expand_platform_vars(environment)
                # Re-fetch the build configuration so we have its expansions.
                build_configuration = bconfig.get_build_configuration(
                    build_configuration.name, platform_name=platform)
                build_directory = bconfig.make_build_directory(
                    build_configuration,
                    platform=platform,
                    dry_run=args.dry_run)
                if not args.dry_run:
                    logger.debug("building in %s" % build_directory)
                    os.chdir(build_directory)
                else:
                    logger.info("building in %s" % build_directory)

                if configure_first:
                    result = _configure_a_configuration(
                        bconfig,
                        build_configuration,
                        args.build_extra_arguments,
                        args.dry_run,
                        environment=environment)
                    if result != 0:
                        raise BuildError(
                            "configuring default configuration returned %d" %
                            result)
                result = _build_a_configuration(
                    bconfig,
                    build_configuration,
                    platform_name=platform,
                    extra_arguments=args.build_extra_arguments,
                    dry_run=args.dry_run,
                    environment=environment)
                # always make clean copy of the build metadata regardless of result
                metadata_file_name = configfile.PACKAGE_METADATA_FILE
                logger.debug("metadata file name: %s" % metadata_file_name)
                if os.path.exists(metadata_file_name):
                    if not args.dry_run:
                        os.unlink(metadata_file_name)
                    else:
                        logger.info("would have replaced %s" %
                                    metadata_file_name)
                if result != 0:
                    raise BuildError("building configuration %s returned %d" %
                                     (build_configuration, result))

                # Create the metadata record for inclusion in the package
                metadata_file = configfile.MetadataDescription(
                    path=metadata_file_name, create_quietly=True)
                # COPY the package description from the configuration: we're
                # going to convert it to metadata format.
                metadata_file.package_description = \
                    configfile.PackageDescription(bconfig.package_description)
                # A metadata package_description has a version attribute
                # instead of a version_file attribute.
                metadata_file.package_description.version = \
                    metadata_file.package_description.read_version_file(build_directory)
                del metadata_file.package_description["version_file"]
                logger.info("built %s version %s" %
                            (metadata_file.package_description.name,
                             metadata_file.package_description.version))
                metadata_file.package_description.platforms = None  # omit data on platform configurations
                metadata_file.platform = platform
                metadata_file.configuration = build_configuration.name
                metadata_file.build_id = build_id
                # get the record of any installed packages
                logger.debug("installed files in " + args.installed_filename)

                # SL-773: This if/else partly replicates
                # common.select_directories() because our build_directory
                # comes from bconfig, which has been $-expanded.
                # The former select_directories() call produced (e.g.)
                # build-vc120-$AUTOBUILD_ADDRSIZE, which didn't exist.
                if args.select_dir:
                    install_dir = args.select_dir
                    logger.debug(
                        "specified metadata directory: {}".format(install_dir))
                else:
                    # packages were written into 'packages' subdir of build directory by default
                    install_dir = os.path.join(build_directory, "packages")
                    logger.debug("metadata in build subdirectory: {}".format(
                        install_dir))

                # load the list of already installed packages
                installed_pathname = os.path.realpath(
                    os.path.join(install_dir, args.installed_filename))
                if os.path.exists(installed_pathname):
                    metadata_file.add_dependencies(installed_pathname)
                else:
                    logger.debug("no installed files found (%s)" %
                                 installed_pathname)
                if args.clean_only and metadata_file.dirty:
                    raise BuildError(
                        "Build depends on local or legacy installables\n" +
                        "  use 'autobuild install --list-dirty' to see problem packages\n"
                        +
                        "  rerun without --clean-only to allow building anyway"
                    )
                if not args.dry_run:
                    metadata_file.save()
        finally:
            os.chdir(current_directory)
    def run(self, args):
        platform=common.get_current_platform()
        metadata = None
        incomplete = ''
        if not args.source_file:
            # no file specified, so assume we are in a build tree and find the 
            # metadata in the current build directory
            logger.info("searching for metadata in the current build tree")
            config_filename = args.config_filename
            config = configfile.ConfigurationDescription(config_filename)
            metadata_file = os.path.join(config.get_build_directory(args.configuration, platform), configfile.PACKAGE_METADATA_FILE)
            if not os.path.exists(metadata_file):
                logger.warning("No complete metadata file found; attempting to use partial data from installed files")
                # get the absolute path to the installed-packages.xml file
                args.all = False
                args.configurations = args.configuration
                install_dirs = common.select_directories(args, config, "install", "getting installed packages",
                                                         lambda cnf:
                                                         os.path.join(config.get_build_directory(cnf, platform), "packages"))
                installed_pathname = os.path.join(os.path.realpath(install_dirs[0]), args.installed_filename)
                if os.path.exists(installed_pathname):
                    # dummy up a metadata object, but don't create the file
                    metadata = configfile.MetadataDescription()
                    # use the package description from the configuration
                    metadata.package_description = config.package_description
                    metadata.add_dependencies(installed_pathname)
                    incomplete = ' (possibly incomplete)'
                else:
                    raise GraphError("No metadata found in current directory")
            else:
                metadata = configfile.MetadataDescription(path=metadata_file)
        elif args.source_file.endswith(".xml"):
            # the specified file is an xml file; assume it is a metadata file
            logger.info("searching for metadata in autobuild package metadata file %s" % args.source_file)
            metadata = configfile.MetadataDescription(path=args.source_file)
            if not metadata:
                raise GraphError("No metadata found in '%s'" % args.source_file)
        else:
            # assume that the file is a package archive and try to get metadata from it
            logger.info("searching for metadata in autobuild package file %s" % args.source_file)
            metadata_stream = extract_metadata_from_package(args.source_file, configfile.PACKAGE_METADATA_FILE)
            if metadata_stream is not None:
                metadata = configfile.MetadataDescription(stream=metadata_stream)
                if not metadata:
                    raise GraphError("No metadata found in archive '%s'" % args.file)
            
        if metadata:
            graph = pydot.Dot(label=metadata['package_description']['name']+incomplete+' dependencies for '+platform, graph_type='digraph')
            graph.set('overlap', 'false')
            graph.set('splines', 'true')
            graph.set('scale', '2')
            graph.set('smoothType', 'spring')
            graph.set('labelloc', 'top')
            graph.set('labeljust', 'center')

            graph.set_node_defaults(shape='box')

            def add_depends(graph, pkg):
                name = pkg['package_description']['name']
                got = graph.get_node(name) # can return a single Node instance, a list of Nodes, or None 
                try:
                    pkg_node = got if got is None or isinstance(got, pydot.Node) else got[0]
                except IndexError: # some versions of pydot may return an empty list instead of None
                    pkg_node = None
                if pkg_node is None:
                    logger.debug(" graph adding package %s" % name)
                    # can't use the dict .get to supply an empty string default for these, 
                    # because the value in the dict is None.
                    pkg_version = pkg['package_description']['version'] if pkg['package_description']['version'] else "";
                    pkg_build_id = pkg['build_id'] if pkg['build_id'] else "";
                    # create the new node with name, version, and build id
                    pkg_node = pydot.Node(name, label="%s\\n%s\\n%s" % (name, pkg_version, pkg_build_id))
                    if 'dirty' in pkg and (pkg['dirty'] == 'True' or pkg['dirty'] is True):
                        logger.debug(" setting %s dirty: %s" % (name, ("missing" if 'dirty' not in pkg else "explicit")))
                        pkg_node.set_shape('ellipse')
                        pkg_node.set_style('dashed')
                    graph.add_node(pkg_node)
                    if 'dependencies' in pkg:
                        for dep_pkg in pkg['dependencies'].itervalues():
                            dep_name = dep_pkg['package_description']['name']
                            dep_node = add_depends(graph, dep_pkg)
                            logger.debug(" graph adding dependency %s -> %s" % (dep_name, name))
                            edge = pydot.Edge(dep_name, name)
                            if 'dirty' in dep_pkg and (dep_pkg['dirty'] == 'True' or dep_pkg['dirty'] is True):
                                edge.set_style('dashed')
                            graph.add_edge(edge)
                return pkg_node

            root = add_depends(graph, metadata)
            root.set_root('true')
            root.set_shape('octagon')

            if args.dot_file:
                try:
                    dot_file=open(args.dot_file,'wb')
                except IOError as err:
                    raise GraphError("Unable to open dot file %s: %s" % (args.dot_file, err))
                dot_file.write(graph.to_string())
                dot_file.close()
                
            if args.display or args.graph_file:
                if args.graph_file:
                    graph_file = args.graph_file
                else:
                    graph_file = os.path.join(tempfile.gettempdir(), 
                                              metadata['package_description']['name'] + "_graph_" 
                                              + args.graph_type + '.png')
                logger.info("writing %s" % graph_file)
                graph.write_png(graph_file, prog=args.graph_type)
                if args.display and not args.graph_file:
                    webbrowser.open('file:'+graph_file)
            else:
                print "%s" % graph.to_string()

        else:
            raise GraphError("No metadata found")