Home | History | Annotate | Download | only in distro-import
      1 #!/usr/bin/python2.4
      2 #
      3 # CDDL HEADER START
      4 #
      5 # The contents of this file are subject to the terms of the
      6 # Common Development and Distribution License (the "License").
      7 # You may not use this file except in compliance with the License.
      8 #
      9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10 # or http://www.opensolaris.org/os/licensing.
     11 # See the License for the specific language governing permissions
     12 # and limitations under the License.
     13 #
     14 # When distributing Covered Code, include this CDDL HEADER in each
     15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16 # If applicable, add the following below this CDDL HEADER, with the
     17 # fields enclosed by brackets "[]" replaced with your own identifying
     18 # information: Portions Copyright [yyyy] [name of copyright owner]
     19 #
     20 # CDDL HEADER END
     21 #
     22 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23 # Use is subject to license terms.
     24 
     25 
     26 import fnmatch
     27 import getopt
     28 import gettext
     29 import os
     30 import pkg.depotcontroller as depotcontroller
     31 import pkg.publish.transaction as trans
     32 import re
     33 import shlex
     34 import sys
     35 import urllib
     36 import urlparse
     37 
     38 from datetime import datetime
     39 from itertools import groupby
     40 from pkg import actions, elf
     41 from pkg.bundle.SolarisPackageDirBundle import SolarisPackageDirBundle
     42 from pkg.sysvpkg import SolarisPackage
     43 from tempfile import mkstemp
     44 
     45 gettext.install("import", "/usr/lib/locale")
     46 
     47 class package(object):
     48         def __init__(self, name):
     49                 self.name = name
     50                 self.files = []
     51                 self.depend = []
     52                 self.file_depend = []
     53                 self.idepend = []     #svr4 pkg deps, if any
     54                 self.undepend = []
     55                 self.extra = []
     56                 self.dropped_licenses = []
     57                 self.nonhollow_dirs = {}
     58                 self.srcpkgs = []
     59                 self.classification = ""
     60                 self.desc = ""
     61                 self.version = ""
     62                 self.imppkg = None
     63                 pkgdict[name] = self
     64 
     65         def import_pkg(self, imppkg, line):
     66                 try:
     67                         p = SolarisPackage(pkg_path(imppkg))
     68                 except:
     69                         raise RuntimeError("No such package: '%s'" % imppkg)
     70 
     71                 self.imppkg = p
     72 
     73                 svr4pkgpaths[p.pkginfo["PKG.PLAT"]] = pkg_path(imppkg)
     74 
     75                 # filename NOT always same as pkgname
     76                 imppkg = p.pkginfo["PKG.PLAT"]
     77                 svr4pkgsseen[imppkg] = p
     78 
     79                 if "SUNW_PKG_HOLLOW" in p.pkginfo and \
     80                     p.pkginfo["SUNW_PKG_HOLLOW"].lower() == "true":
     81                         hollow_pkgs[imppkg] = True
     82 
     83                 excludes = dict((f, True) for f in line.split())
     84 
     85                 # XXX This isn't thread-safe.  We want a dict method that adds
     86                 # the key/value pair, but throws an exception if the key is
     87                 # already present.
     88                 for o in p.manifest:
     89                         if o.pathname in excludes:
     90                                 print "excluding %s from %s" % \
     91                                     (o.pathname, imppkg)
     92                                 continue
     93 
     94                         if o.pathname in elided_files:
     95                                 print "ignoring %s in %s" % (o.pathname, imppkg)
     96                                 continue
     97 
     98                         if o.type == "e":
     99                                 if o.pathname not in editable_files:
    100                                         editable_files[o.pathname] = \
    101                                             [(imppkg, self)]
    102                                 else:
    103                                         editable_files[o.pathname].append(
    104                                             (imppkg, self))
    105 
    106                         # XXX This decidedly ignores "e"-type files.
    107 
    108                         if o.type in "fv" and o.pathname in usedlist:
    109                                 s = reuse_err % (
    110                                         o.pathname,
    111                                         self.name,
    112                                         imppkg,
    113                                         svr4pkgpaths[imppkg],
    114                                         usedlist[o.pathname][1].name,
    115                                         usedlist[o.pathname][0],
    116                                         svr4pkgpaths[usedlist[o.pathname][0]])
    117                                 print s
    118                                 raise RuntimeError(s)
    119                         elif o.type == "i" and o.pathname == "copyright":
    120                                 # Fake up a unique path for each license.
    121                                 o.pathname = "//license/%s" % imppkg
    122                                 usedlist[o.pathname] = (imppkg, self)
    123                                 self.files.append(o)
    124                         elif o.type != "i":
    125                                 if o.type in "dx" and imppkg not in hollow_pkgs:
    126                                         self.nonhollow_dirs[o.pathname] = True
    127 
    128                                 usedlist[o.pathname] = (imppkg, self)
    129                                 self.check_perms(o)
    130                                 self.files.append(o)
    131 
    132                 if not self.version:
    133                         self.version = "%s-%s" % (def_vers,
    134                             get_branch(self.name))
    135                 if not self.desc:
    136                         self.desc = zap_strings(p.pkginfo["NAME"],
    137                             description_detritus)
    138 
    139                 # This is how we'd import dependencies, but we'll use
    140                 # file-specific dependencies only, since these tend to be
    141                 # broken.
    142                 # self.depend.extend(
    143                 #     d.req_pkg_fmri
    144                 #     for d in p.deps
    145                 # )
    146 
    147                 self.add_svr4_src(imppkg)
    148 
    149         def add_svr4_src(self, imppkg):
    150                 if imppkg in destpkgs:
    151                         destpkgs[imppkg].append(self.name)
    152                 else:
    153                         destpkgs[imppkg] = [self.name]
    154                 self.srcpkgs.append(imppkg)
    155 
    156         def import_file(self, fname, line):
    157                 imppkgname = self.imppkg.pkginfo["PKG.PLAT"]
    158 
    159                 if "SUNW_PKG_HOLLOW" in self.imppkg.pkginfo and \
    160                     self.imppkg.pkginfo["SUNW_PKG_HOLLOW"].lower() == "true":
    161                         hollow_pkgs[imppkgname] = True
    162 
    163                 if fname in usedlist:
    164                         t = [
    165                             f for f in usedlist[fname][1].files
    166                             if f.pathname == fname
    167                         ][0].type
    168                         if t in "fv":
    169                                 assert imppkgname == usedlist[fname][0]
    170                                 raise RuntimeError(reuse_err % (
    171                                         fname,
    172                                         self.name,
    173                                         self.imppkg,
    174                                         svr4pkgpaths[self.imppkg],
    175                                         usedlist[fname][1].name,
    176                                         usedlist[fname][0],
    177                                         svr4pkgpaths[usedlist[fname][0]]))
    178 
    179                 usedlist[fname] = (imppkgname, self)
    180                 o = [
    181                     o
    182                     for o in self.imppkg.manifest
    183                     if o.pathname == fname 
    184                 ]
    185                 # There should be only one file with a given pathname in a
    186                 # single package.
    187                 if len(o) != 1:
    188                         print "ERROR: %s %s" % (imppkgname, fname)
    189                         assert len(o) == 1
    190 
    191                 if line:
    192                         a = actions.fromstr(
    193                             "%s path=%s %s" % \
    194                                     (
    195                                         self.convert_type(o[0].type),
    196                                         o[0].pathname,
    197                                         line
    198                                         )
    199                             )
    200                         for attr in a.attrs:
    201                                 if attr == "owner":
    202                                         o[0].owner = a.attrs[attr]
    203                                 elif attr == "group":
    204                                         o[0].group = a.attrs[attr]
    205                                 elif attr == "mode":
    206                                         o[0].mode = a.attrs[attr]
    207                 self.check_perms(o[0])
    208                 self.files.extend(o)
    209 
    210         def convert_type(self, svrtype):
    211                 """ given sv4r type, return IPS type"""
    212                 return {
    213                         "f": "file", "e": "file", "v": "file",
    214                         "d": "dir", "x": "dir",
    215                         "s": "link",
    216                         "l": "hardlink"
    217                         }[svrtype]
    218 
    219         def type_convert(self, ipstype):
    220                 """ given IPS type, return svr4 type(s)"""
    221                 return {
    222                         "file": "fev", "dir": "dx", "link": "s",
    223                         "hardlink": "l"
    224                         }[ipstype]
    225 
    226         def file_to_action(self, f):
    227 
    228                 if f.type in "dx":
    229                         action = actions.directory.DirectoryAction(
    230                             None, mode = f.mode, owner = f.owner,
    231                             group = f.group, path = f.pathname)
    232                 elif f.type in "efv":
    233                         action = actions.file.FileAction(
    234                             None, mode = f.mode, owner = f.owner,
    235                             group = f.group, path = f.pathname)
    236                 elif f.type == "s":
    237                         action = actions.link.LinkAction(None,
    238                             target = f.target, path = f.pathname)
    239                 elif f.type == "l":
    240                         action = actions.hardlink.HardLinkAction(None,
    241                             target = f.target, path = f.pathname)
    242                 else:
    243                         print "unknown type %s - path %s" % \
    244                             ( f.type, f.pathname)
    245 
    246                 return action
    247 
    248         def check_perms(self, manifest):
    249                 if manifest.type not in "fevdxbc":
    250                         return
    251 
    252                 if manifest.owner == "?":
    253                         manifest.owner = "root"
    254                         print "File %s in pkg %s owned by '?': mapping to %s" \
    255                             % (manifest.pathname, self.name, manifest.owner)
    256 
    257                 if manifest.group == "?":
    258                         manifest.group = "bin"
    259                         print "File %s in pkg %s of group '?': mapping to %s" \
    260                             % (manifest.pathname, self.name, manifest.group)
    261                 if manifest.mode == "?":
    262                         manifest.mode = "0444"
    263                         print "File %s in pkg %s mode '?': mapping to %s" % \
    264                             (manifest.pathname, self.name, manifest.mode)
    265 
    266 
    267         def chattr(self, fname, line):
    268                 o = [f for f in self.files if f.pathname == fname]
    269                 if not o:
    270                         raise RuntimeError("No file '%s' in package '%s'" % \
    271                             (fname, curpkg.name))
    272 
    273                 line = line.rstrip()
    274 
    275                 # is this a deletion?
    276                 if line.startswith("drop"):
    277                         for f in o:
    278                                 # deletion of existing attribute
    279                                 if not hasattr(f, "deleted_attrs"):
    280                                         f.deleted_attrs = []
    281                                 print "Adding drop on %s of %s" % \
    282                                     (fname, line.split()[1:])
    283                                 f.deleted_attrs.extend(line.split()[1:])
    284                         return
    285 
    286                 # handle insertion/modification case
    287                 for f in o:
    288                         # create attribute dictionary from line
    289                         new_type = self.convert_type(f.type)
    290                         new_attrs = actions._fromstr("%s %s" % 
    291                             (new_type, line.rstrip()))[2]
    292                         # get path if we're not changing it 
    293                         if "path" not in new_attrs:
    294                                 new_attrs["path"] = fname
    295                         a = actions.types[new_type](**new_attrs)
    296                         if show_debug:
    297                                 print "Updating attributes on " + \
    298                                     "'%s' in '%s' with '%s'" % \
    299                                     (f.pathname, curpkg.name, a)
    300                         orig_action = self.file_to_action(f)
    301 
    302                         if not hasattr(f, "changed_attrs"):
    303                                 f.changed_attrs = {}
    304 
    305                         # each chattr produces a dictionary of actions
    306                         # including a path=xxxx and whatever modifications
    307                         # are made.  Note that the path value may be a list in
    308                         # the case of modifications to the path... since
    309                         # each chattr produces another path= entry and results
    310                         # from applying the changes to the original file spec,
    311                         # we need to ignore path if it hasn't changed... for
    312                         # generality, we ignore all unchanged attributes in
    313                         # the code below, adding into changed_attrs only those
    314                         # that are different from the original... this also
    315                         # insulates us from the possibility of actions.fromstr
    316                         # adding additional attributes in the constructor...
    317 
    318                         for key in a.attrs.keys():
    319                                 if key not in orig_action.attrs or \
    320                                     orig_action.attrs[key] != a.attrs[key]:
    321                                         if key in f.changed_attrs:
    322                                                 print "Warning: overwriting " \
    323                                                     "changed attr %s on %s " \
    324                                                     "from %s to %s" % \
    325                                                     (key, f.pathname,
    326                                                      f.changed_attrs[key],
    327                                                      a.attrs[key])
    328                                         f.changed_attrs[key] = a.attrs[key]
    329 
    330 
    331         # apply a chattr to wildcarded files/dirs
    332         # also allows package specification, wildcarding, regexp edit
    333 
    334         def chattr_glob(self, glob, line):
    335                 args = line.split()
    336                 if args[0] == "from":
    337                         args.pop(0)
    338                         pkgglob = args.pop(0)
    339                         line = " ".join(args)
    340                 else:
    341                         pkgglob = "*"
    342 
    343                 if args[0] == "type": # we care about type
    344                         args.pop(0)
    345                         types = self.type_convert(args.pop(0))
    346                         line = " ".join(args)
    347                 else:
    348                         types = "dfevslx"
    349 
    350                 if args[0] == "edit": # we're doing regexp edit of attr
    351                         edit = True
    352                         args.pop(0)
    353                         target = args.pop(0)
    354                         regexp = re.compile(args.pop(0))
    355                         replace = args.pop(0)
    356                         line = " ".join(args)
    357                 else:
    358                         edit = False
    359 
    360                 o = [
    361                         f
    362                         for f in self.files
    363                         if fnmatch.fnmatchcase(f.pathname, glob) and
    364                             fnmatch.fnmatchcase(
    365                                 usedlist[f.pathname][0], pkgglob) and
    366                             f.type in types
    367                      ]
    368 
    369                 chattr_line = line
    370 
    371                 for f in o:
    372                         fname = f.pathname
    373                         orig_action = self.file_to_action(f)
    374                         if edit:
    375                                 if target in orig_action.attrs:
    376                                         old_value = orig_action.attrs[target]
    377                                         new_value = regexp.sub(replace, \
    378                                             old_value)
    379                                         if old_value == new_value:
    380                                                 continue
    381                                         chattr_line = "%s=%s %s" % \
    382                                             (target, new_value, line)
    383                                 else:
    384                                         continue
    385                         chattr_line = chattr_line.rstrip()
    386                         if show_debug:
    387                                 print "Updating attributes on " + \
    388                                     "'%s' in '%s' with '%s'" % \
    389                                     (fname, curpkg.name, chattr_line)
    390 
    391                         # create attribute dictionary from line
    392                         new_type = self.convert_type(f.type)
    393                         new_attrs = actions._fromstr("%s %s" % 
    394                             (new_type, chattr_line.rstrip()))[2]
    395                         # get path if we're not changing it 
    396                         if "path" not in new_attrs:
    397                                 new_attrs["path"] = fname
    398                         a = actions.types[new_type](**new_attrs)
    399                         # each chattr produces a dictionary of actions
    400                         # including a path=xxxx and whatever modifications
    401                         # are made.  Note that the path value may be a list in
    402                         # the case of modifications to the path... since
    403                         # each chattr produces another path= entry and results
    404                         # from applying the changes to the original file spec,
    405                         # we need to ignore path if it hasn't changed... for
    406                         # generality, we ignore all unchanged attributes in
    407                         # the code below, adding into changed_attrs only those
    408                         # that are different from the original... this also
    409                         # insulates us from the possibility of actions.fromstr
    410                         # adding additional attributes in the constructor...
    411 
    412                         if not hasattr(f, "changed_attrs"):
    413                                 f.changed_attrs = {}
    414                         for key in a.attrs.keys():
    415                                 if key not in orig_action.attrs or \
    416                                     orig_action.attrs[key] != a.attrs[key]:
    417                                         if key in f.changed_attrs:
    418                                                 print "Warning: overwriting " \
    419                                                     "changed attr %s on %s " \
    420                                                     "from %s to %s" % \
    421                                                     (key, f.pathname,
    422                                                      f.changed_attrs[key],
    423                                                      a.attrs[key])
    424                                         f.changed_attrs[key] = a.attrs[key]
    425 
    426 pkgpaths = {}
    427 
    428 def pkg_path(pkgname):
    429         name = os.path.basename(pkgname)
    430         if pkgname in pkgpaths:
    431                 return pkgpaths[name]
    432         if "/" in pkgname:
    433                 pkgpaths[name] = os.path.realpath(pkgname)
    434                 return pkgname
    435         else:
    436                 for each_path in wos_path:
    437                         if os.path.exists(each_path + "/" + pkgname):
    438                                 pkgpaths[name] = each_path + "/" + pkgname
    439                                 return pkgpaths[name]
    440 
    441                 raise RuntimeError("package %s not found" % pkgname)
    442 
    443 
    444 def start_package(pkgname):
    445         return package(pkgname)
    446 
    447 def end_package(pkg):
    448         pkg_branch = get_branch(pkg.name)
    449         if not pkg.version:
    450                 pkg.version = "%s-%s" % (def_vers, pkg_branch)
    451         elif "-" not in pkg.version:
    452                 pkg.version += "-%s" % pkg_branch
    453 
    454         print "Package '%s'" % pkg.name
    455         print "  Version:", pkg.version
    456         print "  Description:", pkg.desc
    457         print "  Classification: ", pkg.classification
    458 
    459 def publish_pkg(pkg):
    460 
    461         new_pkg_name = "%s@%s" % (pkg.name, pkg.version)
    462         t = trans.Transaction(def_repo, create_repo=create_repo,
    463             pkg_name=new_pkg_name, noexecute=nopublish)
    464 
    465         print "    open %s" % new_pkg_name
    466         transaction_id = t.open()
    467 
    468         # Publish non-file objects first: they're easy.
    469         for f in pkg.files:
    470                 if f.type in "dx":
    471                         action = actions.directory.DirectoryAction(
    472                             None, mode = f.mode, owner = f.owner,
    473                             group = f.group, path = f.pathname)
    474                         if hasattr(f, "changed_attrs"):
    475                                 action.attrs.update(f.changed_attrs)
    476                                 # chattr may have produced two path values
    477                                 action.attrs["path"] = \
    478                                     action.attrlist("path")[-1]
    479                         print "    %s add dir %s %s %s %s" % (
    480                                 pkg.name,
    481                                 action.attrs["mode"],
    482                                 action.attrs["owner"],
    483                                 action.attrs["group"],
    484                                 action.attrs["path"]
    485                                 )
    486                 elif f.type == "s":
    487                         action = actions.link.LinkAction(None,
    488                             target = f.target, path = f.pathname)
    489                         if hasattr(f, "changed_attrs"):
    490                                 action.attrs.update(f.changed_attrs)
    491                                 # chattr may have produced two path values
    492                                 action.attrs["path"] = \
    493                                     action.attrlist("path")[-1]
    494                         print "    %s add link %s %s" % (
    495                                 pkg.name,
    496                                 action.attrs["path"],
    497                                 action.attrs["target"]
    498                                 )
    499                 elif f.type == "l":
    500                         action = actions.hardlink.HardLinkAction(None,
    501                             target = f.target, path = f.pathname)
    502                         if hasattr(f, "changed_attrs"):
    503                                 action.attrs.update(f.changed_attrs)
    504                                 # chattr may have produced two path values
    505                                 action.attrs["path"] = \
    506                                     action.attrlist("path")[-1]
    507                         pkg.depend += process_link_dependencies(
    508                             action.attrs["path"], action.attrs["target"])
    509                         print "    %s add hardlink %s %s" % (
    510                                 pkg.name,
    511                                 action.attrs["path"],
    512                                 action.attrs["target"]
    513                                 )
    514                 else:
    515                         continue
    516 
    517                 #
    518                 # If the originating package was hollow, tag this file
    519                 # as being global zone only.
    520                 #
    521 
    522                 if f.type not in "dx" and f.pathname in usedlist and \
    523                     usedlist[f.pathname][0] in hollow_pkgs:
    524                         action.attrs["opensolaris.zone"] = "global"
    525                         action.attrs["variant.opensolaris.zone"] = "global"
    526 
    527                 if f.type in "dx" and f.pathname in usedlist and \
    528                     usedlist[f.pathname][0] in hollow_pkgs and \
    529                     f.pathname not in pkg.nonhollow_dirs:
    530                         action.attrs["opensolaris.zone"] = "global"
    531                         action.attrs["variant.opensolaris.zone"] = "global"
    532 
    533                 # handle attribute deletion
    534                 if hasattr(f, "deleted_attrs"):
    535                         for d in f.deleted_attrs:
    536                                 if d in action.attrs:
    537                                         del action.attrs[d]
    538 
    539                 t.add(action)
    540 
    541         # Group the files in a (new) package based on what (old) package they
    542         # came from, so that we can iterate through all files in a single (old)
    543         # package (and, therefore, in a single bzip2 archive) before moving on
    544         # to the next.  Because groupby() needs its input pre-sorted by group
    545         # and we want to maintain the order that the files come out of the cpio
    546         # archives, we coalesce the groups with the groups dictionary.
    547         def fn(key):
    548                 return usedlist[key.pathname][0]
    549         groups = {}
    550         for k, g in groupby((f for f in pkg.files if f.type in "fevi"), fn):
    551                 if k in groups:
    552                         groups[k].extend(g)
    553                 else:
    554                         groups[k] = list(g)
    555 
    556         def otherattrs(action):
    557                 s = " ".join(
    558                     "%s=%s" % (a, action.attrs[a])
    559                     for a in action.attrs
    560                     if a not in ("owner", "group", "mode", "path")
    561                 )
    562                 if s:
    563                         return " " + s
    564                 else:
    565                         return ""
    566 
    567         # Maps class names to preserve attribute values.
    568         preserve_dict = {
    569             "renameold": "renameold",
    570             "renamenew": "renamenew",
    571             "preserve": "true",
    572             "svmpreserve": "true"
    573         }
    574 
    575         undeps = set()
    576         for g in groups.values():
    577                 pkgname = usedlist[g[0].pathname][0]
    578                 print "pulling files from archive in package", pkgname
    579                 bundle = SolarisPackageDirBundle(svr4pkgpaths[pkgname])
    580                 pathdict = dict((f.pathname, f) for f in g)
    581                 for f in bundle:
    582                         if f.name == "license":
    583                                 if f.attrs["license"] in pkg.dropped_licenses:
    584                                         continue
    585                                 # add transaction id so that every version
    586                                 # of a pkg will have a unique license to prevent
    587                                 # license from disappearing on upgrade
    588                                 f.attrs["transaction_id"] = transaction_id
    589                                 # The "path" attribute is confusing and
    590                                 # unnecessary for licenses.
    591                                 del f.attrs["path"]
    592                                 print "    %s add license %s" % \
    593                                     (pkg.name, f.attrs["license"])
    594                                 t.add(f)
    595                         elif f.attrs["path"] in pathdict:
    596                                 if pkgname in hollow_pkgs:
    597                                         f.attrs["opensolaris.zone"] = "global"
    598                                         f.attrs["variant.opensolaris.zone"] = \
    599                                             "global"
    600                                 path = f.attrs["path"]
    601                                 if pathdict[path].type in "ev":
    602                                         f.attrs["preserve"] = "true"
    603                                 f.attrs["owner"] = pathdict[path].owner
    604                                 f.attrs["group"] = pathdict[path].group
    605                                 f.attrs["mode"] = pathdict[path].mode
    606 
    607                                 # is this a file for which we need a timestamp?
    608                                 basename = os.path.basename(path)
    609                                 for file_pattern in timestamp_files:
    610                                         if fnmatch.fnmatch(basename,
    611                                             file_pattern):
    612                                                 break
    613                                 else:
    614                                         del f.attrs["timestamp"]
    615                                 if pathdict[path].klass in preserve_dict.keys():
    616                                         f.attrs["preserve"] = \
    617                                             preserve_dict[pathdict[path].klass]
    618                                 if hasattr(pathdict[path], "changed_attrs"):
    619                                         f.attrs.update(
    620                                             pathdict[path].changed_attrs)
    621                                         # chattr may have produced two values
    622                                         f.attrs["path"] = f.attrlist("path")[-1]
    623 
    624                                 print "    %s add file %s %s %s %s%s" % \
    625                                     (pkg.name, f.attrs["mode"],
    626                                         f.attrs["owner"], f.attrs["group"],
    627                                         f.attrs["path"], otherattrs(f))
    628 
    629                                 # handle attribute deletion
    630                                 if hasattr(pathdict[path], "deleted_attrs"):
    631                                         for d in pathdict[path].deleted_attrs:
    632                                                 if d in f.attrs:
    633                                                         print "removed %s from %s in pkg %s" % (d, path, new_pkg_name)
    634                                                         del f.attrs[d]
    635 
    636                                 # Read the file in chunks to avoid a memory
    637                                 # footprint blowout.
    638                                 fo = f.data()
    639                                 bufsz = 256 * 1024
    640                                 sz = int(f.attrs["pkg.size"])
    641                                 fd, tmp = mkstemp(prefix="pkg.")
    642                                 while sz > 0:
    643                                         d = fo.read(min(bufsz, sz))
    644                                         os.write(fd, d)
    645                                         sz -= len(d)
    646                                 d = None
    647                                 os.close(fd)
    648 
    649                                 # Fool the action into pulling from a
    650                                 # temporary file so that both add() and
    651                                 # process_dependencies() can read() the
    652                                 # data.
    653                                 f.data = lambda: open(tmp, "rb")
    654                                 t.add(f)
    655 
    656                                 # Look for dependencies
    657                                 deps, u = process_dependencies(tmp, path)
    658                                 pkg.depend += deps
    659                                 if u:
    660                                         print \
    661                                             "%s has missing dependencies: %s" \
    662                                             % (path, u)
    663                                 undeps |= set(u)
    664                                 os.unlink(tmp)
    665 
    666         # process any dependencies on files
    667         for f in pkg.file_depend:
    668                 f = f.lstrip("/") # remove any leading /                
    669                 if f in usedlist:
    670                         pkg.depend += [ "%s@%s" %
    671                             (usedlist[f][1].name,
    672                              usedlist[f][1].version) 
    673                         ]
    674                 else:
    675                         print "Warning: pkg %s: depend_path %s not satisfied" \
    676                             % (pkg.name, f)
    677                         undeps.add(f)
    678         # Publish dependencies
    679 
    680         missing_cnt = 0
    681 
    682         for p in set(pkg.idepend): # over set of svr4 deps, append ipkgs
    683                 if p in destpkgs:
    684                         pkg.depend.extend(destpkgs[p])
    685                 else:
    686                         print "pkg %s: SVR4 package %s not seen" % \
    687                             (pkg.name, p)
    688                         missing_cnt += 1
    689         if missing_cnt > 0:
    690                 raise RuntimeError("missing packages!")
    691 
    692         for p in set(pkg.depend) - set(pkg.undepend):
    693                 # Don't make a package depend on itself.
    694                 if p.split("@")[0] == pkg.name:
    695                         continue
    696                 # enhance unqualified dependencies to include current
    697                 # pkg version
    698                 if "@" not in p and p in pkgdict:
    699                         p = "%s@%s" % (p, pkgdict[p].version)
    700 
    701                 print "    %s add depend require %s" % (pkg.name, p)
    702                 action = actions.depend.DependencyAction(None,
    703                     type = "require", fmri = p)
    704                 t.add(action)
    705 
    706         for a in pkg.extra:
    707                 print "    %s add %s" % (pkg.name, a)
    708                 action = actions.fromstr(a)
    709                 if hasattr(action, "hash"):
    710                         fname, fd = sourcehook(action.hash)
    711                         fd.close()
    712                         action.data = lambda: file(fname, "rb")
    713                         action.attrs["pkg.size"] = str(os.stat(fname).st_size)
    714                         if action.name == "license":
    715                                 action.attrs["transaction_id"] = transaction_id
    716                 #
    717                 # fmris may not be completely specified; enhance them to current
    718                 # version if this is the case
    719                 #
    720                 for attr in action.attrs:
    721                         if attr == "fmri" and \
    722                             "@" not in action.attrs[attr] and \
    723                             action.attrs[attr][5:] in pkgdict:
    724                                 action.attrs[attr] += "@%s" % \
    725                                     pkgdict[action.attrs[attr][5:]].version
    726                 t.add(action)
    727 
    728         if pkg.desc:
    729                 print "    %s add set description=%s" % (pkg.name, pkg.desc)
    730                 action = actions.attribute.AttributeAction(None,
    731                     description = pkg.desc)
    732                 t.add(action)
    733 
    734         if pkg.classification:
    735                 print "    %s add set info.classification=%s" % \
    736                     (pkg.name, pkg.classification)
    737                 attrs = dict(name="info.classification",
    738                              value=pkg.classification)
    739                 action = actions.attribute.AttributeAction(None, **attrs)
    740                 t.add(action)
    741 
    742         if pkg.name != "SUNWipkg":
    743                 for p in pkg.srcpkgs:
    744                         try:
    745                                 sp = svr4pkgsseen[p]
    746                         except KeyError:
    747                                 continue
    748 
    749                         wanted_attrs = (
    750                                 "PKG", "NAME", "ARCH", "VERSION", "CATEGORY",
    751                                 "VENDOR", "DESC", "HOTLINE"
    752                                 )
    753                         attrs = dict(
    754                                 (k.lower(), v)
    755                                 for k, v in sp.pkginfo.iteritems()
    756                                 if k in wanted_attrs
    757                                 )
    758                         attrs["pkg"] = sp.pkginfo["PKG.PLAT"]
    759 
    760                         action = actions.legacy.LegacyAction(None, **attrs)
    761 
    762                         print "    %s add %s" % (pkg.name, action)
    763                         t.add(action)
    764 
    765         if undeps:
    766                 print "Missing dependencies:", list(undeps)
    767 
    768         print "    close"
    769         pkg_fmri, pkg_state = t.close(refresh_index=not defer_refresh)
    770         print "%s: %s\n" % (pkg_fmri, pkg_state)
    771 
    772 def process_link_dependencies(path, target):
    773         orig_target = target
    774         if target[0] != "/":
    775                 target = os.path.normpath(
    776                     os.path.join(os.path.split(path)[0], target))
    777 
    778         if target in usedlist:
    779                 if show_debug:
    780                         print "hardlink %s -> %s makes %s depend on %s" % \
    781                             (
    782                                 path, orig_target,
    783                                 usedlist[path][1].name,
    784                                 usedlist[target][1].name
    785                                 )
    786                 return ["%s@%s" % (usedlist[target][1].name,
    787                     usedlist[target][1].version)]
    788         else:
    789                 return []
    790 
    791 def process_dependencies(fname, path):
    792         if not elf.is_elf_object(fname):
    793                 return process_non_elf_dependencies(fname, path)
    794 
    795         ei = elf.get_info(fname)
    796         try:
    797                 ed = elf.get_dynamic(fname)
    798         except elf.ElfError:
    799                 deps = []
    800                 rp = []
    801         else:
    802                 deps = [
    803                     d[0]
    804                     for d in ed.get("deps", [])
    805                 ]
    806                 rp = ed.get("runpath", "").split(":")
    807                 if len(rp) == 1 and rp[0] == "":
    808                         rp = []
    809 
    810         rp = [
    811             os.path.normpath(p.replace("$ORIGIN", "/" + os.path.dirname(path)))
    812             for p in rp
    813         ]
    814 
    815         kernel64 = None
    816 
    817         # For kernel modules, default path resolution is /platform/<platform>,
    818         # /kernel, /usr/kernel.  But how do we know what <platform> would be for
    819         # a given module?  Does it do fallbacks to, say, sun4u?
    820         if path.startswith("kernel") or path.startswith("usr/kernel") or \
    821             (path.startswith("platform") and path.split("/")[2] == "kernel"):
    822                 if rp:
    823                         print "RUNPATH set for kernel module (%s): %s" % \
    824                             (path, rp)
    825                 # Default kernel search path
    826                 rp.extend(("/kernel", "/usr/kernel"))
    827                 # What subdirectory should we look in for 64-bit kernel modules?
    828                 if ei["bits"] == 64:
    829                         if ei["arch"] == "i386":
    830                                 kernel64 = "amd64"
    831                         elif ei["arch"] == "sparc":
    832                                 kernel64 = "sparcv9"
    833                         else:
    834                                 print ei["arch"]
    835         else:
    836                 if "/lib" not in rp:
    837                         rp.append("/lib")
    838                 if "/usr/lib" not in rp:
    839                         rp.append("/usr/lib")
    840 
    841         # XXX Do we need to handle anything other than $ORIGIN?  x86 images have
    842         # a couple of $PLATFORM and $ISALIST instances.
    843         for p in rp:
    844                 if "$" in p:
    845                         tok = p[p.find("$"):]
    846                         if "/" in tok:
    847                                 tok = tok[:tok.find("/")]
    848                         print "%s has dynamic token %s in rpath" % (path, tok)
    849 
    850         dep_pkgs = []
    851         undeps = []
    852         depend_list = []
    853         for d in deps:
    854                 for p in rp:
    855                         # The instances of "[1:]" below are because usedlist
    856                         # stores paths without leading slash
    857                         if kernel64:
    858                                 # Find 64-bit modules the way krtld does.
    859                                 # XXX We don't resolve dependencies found in
    860                                 # /platform, since we don't know where under
    861                                 # /platform to look.
    862                                 head, tail = os.path.split(d)
    863                                 deppath = os.path.join(p,
    864                                                        head,
    865                                                        kernel64,
    866                                                        tail)[1:]
    867                         else:
    868                                 # This is a hack for when a runpath uses the 64
    869                                 # symlink to the actual 64-bit directory.
    870                                 # Better would be to see if the runpath was a
    871                                 # link, and if so, use its resolution, but
    872                                 # extracting that information from used list is
    873                                 # a pain, especially because you potentially
    874                                 # have to resolve symlinks at all levels of the
    875                                 # path.
    876                                 if p.endswith("/64"):
    877                                         if ei["arch"] == "i386":
    878                                                 p = p[:-2] + "amd64"
    879                                         elif ei["arch"] == "sparc":
    880                                                 p = p[:-2] + "sparcv9"
    881                                 deppath = os.path.join(p, d)[1:]
    882                         if deppath in usedlist:
    883                                 dep_pkgs += [ "%s@%s" %
    884                                     (usedlist[deppath][1].name,
    885                                     usedlist[deppath][1].version) ]
    886                                 depend_list.append(
    887                                         (
    888                                                 deppath,
    889                                                 usedlist[deppath][1].name
    890                                                 )
    891                                         )
    892                                 break
    893                 else:
    894                         undeps += [ d ]
    895 
    896         if show_debug:
    897                 print "%s makes %s depend on %s" % \
    898                     (path, usedlist[path][1].name, depend_list)
    899 
    900         return dep_pkgs, undeps
    901 
    902 def process_non_elf_dependencies(localpath, path):
    903         # localpath is path to actual file
    904         # path is path in installed image
    905         # take 1
    906         dep_pkgs = []
    907         undeps = []
    908 
    909         f = file(localpath)
    910         l = f.readline()
    911         f.close()
    912 
    913         # add #!/ dependency
    914         if l.startswith("#!/"):
    915                 # usedlist omits leading /
    916                 p = (l[2:].split()[0]) # first part of string is path (removes options)
    917                 # we don't handle dependencies through links, so fix up the common one
    918                 if p.startswith("/bin"):
    919                         p = "/usr" + p
    920                 if p[1:] in usedlist:
    921                         dep_pkgs += [ "%s@%s" % (
    922                             usedlist[p[1:]][1].name,
    923                             usedlist[p[1:]][1].version) 
    924                         ]    
    925                         print "Added dependency on %s because of %s" % (usedlist[p[1:]][1].name, p)
    926                 else:
    927                         undeps = [ p ]
    928                         
    929         return dep_pkgs, undeps
    930 
    931 def zap_strings(instr, strings):
    932         """takes an input string and a list of strings to be removed, ignoring
    933         case"""
    934         for s in strings:
    935                 ls = s.lower()
    936                 while True:
    937                         li = instr.lower()
    938                         i = li.find(ls)
    939                         if i < 0:
    940                                 break
    941                         instr = instr[0:i] + instr[i + len(ls):]
    942         return instr 
    943 
    944 def get_branch(name):
    945         return branch_dict.get(name, def_branch)
    946 
    947 def_vers = "0.5.11"
    948 def_branch = ""
    949 def_wos_path = ["/net/netinstall.eng/export/nv/x/latest/Solaris_11/Product"]
    950 create_repo = False
    951 nopublish = False
    952 show_debug = False
    953 print_pkg_names = False
    954 def_repo = "http://localhost:10000"
    955 wos_path = []
    956 include_path = []
    957 branch_dict = {}
    958 timestamp_files = []
    959 
    960 #
    961 # files (by path) we always delete for bulk imports
    962 # note that we ignore these if specifically included.
    963 #
    964 elided_files = {}
    965 #
    966 # if user uses -j, just_these_pkgs becomes list of pkgs to process
    967 # allowing other arguments to be read in as files...
    968 #
    969 just_these_pkgs = []
    970 #
    971 # strings to rip out of descriptions (case insensitve)
    972 #
    973 description_detritus = [", (usr)", ", (root)", " (usr)", " (root)",
    974 " (/usr)", " - / filesystem", ",root(/)"]
    975 #
    976 # list of global includes to add to every package
    977 #
    978 global_includes = []
    979 # list of macro substitutions
    980 macro_definitions = {}
    981 
    982 try:
    983         _opts, _args = getopt.getopt(sys.argv[1:], "B:D:I:G:NT:b:dj:m:ns:v:w:p:")
    984 except getopt.GetoptError, _e:
    985         print "unknown option", _e.opt
    986         sys.exit(1)
    987 
    988 g_proto_area = os.environ.get("ROOT", "")
    989 
    990 for opt, arg in _opts:
    991         if opt == "-b":
    992                 def_branch = arg.rstrip("abcdefghijklmnopqrstuvwxyz")
    993         elif opt == "-d":
    994                 show_debug = True
    995         elif opt == "-j": # means we're using the new argument form...
    996                 just_these_pkgs.append(arg)
    997         elif opt == "-m":
    998                 _a = arg.split("=", 1)
    999                 macro_definitions.update([("$(%s)" % _a[0], _a[1])])
   1000         elif opt == "-n":
   1001                 nopublish = True
   1002         elif opt == "-p":
   1003                 if not os.path.exists(arg):
   1004                         raise RuntimeError("Invalid prototype area specified.")
   1005                 # Clean up relative ../../, etc. out of path to proto
   1006                 g_proto_area = os.path.realpath(arg)
   1007         elif  opt == "-s":
   1008                 def_repo = arg
   1009                 if def_repo.startswith("file://"):
   1010                         # When publishing to file:// repositories, automatically
   1011                         # create the target repository if needed.
   1012                         create_repo = True
   1013         elif opt == "-v":
   1014                 def_vers = arg
   1015         elif opt == "-w":
   1016                 wos_path.append(arg)
   1017         elif opt == "-D":
   1018                 elided_files[arg] = True
   1019         elif opt == "-I":
   1020                 include_path.extend(arg.split(":"))
   1021         elif opt == "-B":
   1022                 branch_file = file(arg)
   1023                 for _line in branch_file:
   1024                         if not _line.startswith("#"):
   1025                                 bfargs = _line.split()
   1026                                 if len(bfargs) == 2:
   1027                                         branch_dict[bfargs[0]] = bfargs[1]
   1028                 branch_file.close()
   1029         elif opt == "-G": #another file of global includes
   1030                 global_includes.append(arg)
   1031         elif opt == "-N":
   1032                 print_pkg_names = True
   1033         elif opt == "-T":
   1034                 timestamp_files.append(arg)
   1035 
   1036 if not def_branch:
   1037         print "need a branch id (build number)"
   1038         sys.exit(1)
   1039 elif "." not in def_branch:
   1040         print "branch id needs to be of the form 'x.y'"
   1041         sys.exit(1)
   1042 
   1043 if not _args:
   1044         print "need argument!"
   1045         sys.exit(1)
   1046 
   1047 if not wos_path:
   1048         wos_path = def_wos_path
   1049 
   1050 if just_these_pkgs:
   1051         filelist = _args
   1052 else:
   1053         filelist = _args[0:1]
   1054         just_these_pkgs = _args[1:]
   1055 
   1056 
   1057 in_multiline_import = False
   1058 
   1059 # This maps what files we've seen to a tuple of what packages they came from and
   1060 # what packages they went into, so we can prevent more than one package from
   1061 # grabbing the same file.
   1062 usedlist = {}
   1063 
   1064 #
   1065 # pkgdict contains ipkgs by name
   1066 #
   1067 pkgdict = {}
   1068 
   1069 #
   1070 # destpkgs contains the list of ipkgs generated from each svr4 pkg
   1071 # this is needed to generate metaclusters
   1072 #
   1073 destpkgs = {}
   1074 
   1075 #
   1076 #svr4 pkgs seen - pkgs indexed by name
   1077 #
   1078 svr4pkgsseen = {}
   1079 
   1080 #
   1081 #paths where we found the packages we need
   1082 #
   1083 svr4pkgpaths = {}
   1084 
   1085 #
   1086 # editable files and where they're found
   1087 #
   1088 editable_files = {}
   1089 
   1090 #
   1091 # hollow svr4 packages processed
   1092 #
   1093 hollow_pkgs = {}
   1094 
   1095 
   1096 reuse_err = \
   1097     "Conflict in path %s: IPS %s SVR4 %s from %s with IPS %s SVR4 %s from %s"
   1098 
   1099 
   1100 # First pass: don't actually publish anything, because we're not collecting
   1101 # dependencies here.
   1102 def read_full_line(lexer, continuation='\\'):
   1103         """Read a complete line, allowing for the possibility of it being
   1104         continued over multiple lines.  Returns a single joined line, with
   1105         continuation characters and leading and trailing spaces removed.
   1106         """
   1107 
   1108         lines = []
   1109         while True:
   1110                 line = lexer.instream.readline().strip()
   1111                 lexer.lineno = lexer.lineno + 1
   1112                 if line[-1] in continuation:
   1113                         lines.append(line[:-1])
   1114                 else:
   1115                         lines.append(line)
   1116                         break
   1117 
   1118         return apply_macros(' '.join(lines))
   1119 
   1120 def apply_macros(s):
   1121         """Apply macro subs defined on command line... keep applying
   1122         macros until no translations are found.  If macro translates
   1123         to a comment, replace entire token text."""
   1124         while s and "$(" in s:
   1125                 for key in macro_definitions.keys():
   1126                         if key in s:
   1127                                 value = macro_definitions[key]
   1128                                 if value == "#": # comment character
   1129                                         s = "#"  # affects whole token
   1130                                         break
   1131                                 s = s.replace(key, value)
   1132                                 break # look for more substitutions
   1133                 else:
   1134                         break # no more substitutable tokens
   1135         return s
   1136 
   1137 def sourcehook(filename):
   1138         """ implement include hierarchy """
   1139         for i in include_path:
   1140                 f = os.path.join(i, filename)
   1141                 if os.path.exists(f):
   1142                         return (f, open(f))
   1143 
   1144         return filename, open(filename)
   1145 
   1146 class tokenlexer(shlex.shlex):
   1147         def read_token(self):
   1148                 """ simple replacement of $(ARCH) with a non-special
   1149                 value defined on the command line is trivial.  Since
   1150                 shlex's read_token routine also strips comments and
   1151                 white space, this read_token cannot return either 
   1152                 one so any macros that translate to either spaces or
   1153                 # (comment) need to be removed from the token stream."""
   1154 
   1155                 while True:
   1156                         s = apply_macros(shlex.shlex.read_token(self))
   1157                         if s == "#": # discard line if comment; try again
   1158                                 self.instream.readline()
   1159                                 self.lineno = self.lineno + 1
   1160                         # bail on EOF or not space; loop on space
   1161                         elif s == None or (s != "" and not s.isspace()):
   1162                                 break
   1163                 return s
   1164 
   1165 curpkg = None
   1166 def SolarisParse(mf):
   1167         global curpkg
   1168         global in_multiline_import
   1169 
   1170         lexer = tokenlexer(file(mf), mf, True)
   1171         lexer.whitespace_split = True
   1172         lexer.source = "include"
   1173         lexer.sourcehook = sourcehook
   1174 
   1175         while True:
   1176                 token = lexer.get_token()
   1177 
   1178                 if not token:
   1179                         break
   1180 
   1181                 if token == "package":
   1182                         curpkg = start_package(lexer.get_token())
   1183 
   1184                         if print_pkg_names:
   1185                                 print "-j %s" % curpkg.name
   1186 
   1187                 elif token == "end":
   1188                         endarg = lexer.get_token()
   1189                         if endarg == "package":
   1190                                 if print_pkg_names:
   1191                                         curpkg = None
   1192                                         continue
   1193 
   1194                                 for filename in global_includes:
   1195                                         for i in include_path:
   1196                                                 f = os.path.join(i, filename)
   1197                                                 if os.path.exists(f):
   1198                                                         SolarisParse(f)
   1199                                                         break
   1200                                         else:
   1201                                                 raise RuntimeError("File not "
   1202                                                     "found: %s" % filename)
   1203                                 try:
   1204                                         end_package(curpkg)
   1205                                 except Exception, e:
   1206                                         print "ERROR(end_pkg):", e
   1207 
   1208                                 curpkg = None
   1209                         if endarg == "import":
   1210                                 in_multiline_import = False
   1211                                 curpkg.imppkg = None
   1212 
   1213                 elif token == "version":
   1214                         curpkg.version = lexer.get_token()
   1215 
   1216                 elif token == "import":
   1217                         package_name = lexer.get_token()
   1218                         next = lexer.get_token()
   1219                         if next != "exclude":
   1220                                 line = ""
   1221                                 lexer.push_token(next)
   1222                         else:
   1223                                 line = read_full_line(lexer)
   1224 
   1225                         if not print_pkg_names:
   1226                                 curpkg.import_pkg(package_name, line)
   1227 
   1228                 elif token == "from":
   1229                         pkgspec = lexer.get_token()
   1230                         if not print_pkg_names:
   1231                                 p = SolarisPackage(pkg_path(pkgspec))
   1232                                 curpkg.imppkg = p
   1233                                 spkgname = p.pkginfo["PKG.PLAT"]
   1234                                 svr4pkgpaths[spkgname] = pkg_path(pkgspec)
   1235                                 svr4pkgsseen[spkgname] = p
   1236                                 curpkg.add_svr4_src(spkgname)
   1237 
   1238                         junk = lexer.get_token()
   1239                         assert junk == "import"
   1240                         in_multiline_import = True
   1241 
   1242                 elif token == "classification":
   1243                         cat_subcat = lexer.get_token()
   1244                         curpkg.classification = \
   1245                             "org.opensolaris.category.2008:%s" % cat_subcat
   1246 
   1247                 elif token == "description":
   1248                         curpkg.desc = lexer.get_token()
   1249 
   1250                 elif token == "depend":
   1251                         curpkg.depend.append(lexer.get_token())
   1252 
   1253                 elif token == "depend_path":
   1254                         curpkg.file_depend.append(lexer.get_token())
   1255 
   1256                 elif token == "cluster":
   1257                         curpkg.add_svr4_src(lexer.get_token())
   1258 
   1259                 elif token == "idepend":
   1260                         curpkg.idepend.append(lexer.get_token())
   1261 
   1262                 elif token == "undepend":
   1263                         curpkg.undepend.append(lexer.get_token())
   1264 
   1265                 elif token == "add":
   1266                         curpkg.extra.append(read_full_line(lexer))
   1267 
   1268                 elif token == "drop":
   1269                         f = lexer.get_token()
   1270                         if print_pkg_names:
   1271                                 continue
   1272                         l = [o for o in curpkg.files if o.pathname == f]
   1273                         if not l:
   1274                                 print "Cannot drop '%s' from '%s': not " \
   1275                                     "found" % (f, curpkg.name)
   1276                         else:
   1277                                 del curpkg.files[curpkg.files.index(l[0])]
   1278                                 # XXX The problem here is that if we do this on
   1279                                 # a shared file (directory, etc), then it's
   1280                                 # missing from usedlist entirely, since we don't
   1281                                 # keep around *all* packages delivering a shared
   1282                                 # file, just the last seen.  This probably
   1283                                 # doesn't matter much.
   1284                                 del usedlist[f]
   1285 
   1286                 elif token == "drop_license":
   1287                         curpkg.dropped_licenses.append(lexer.get_token())
   1288 
   1289                 elif token == "chattr":
   1290                         fname = lexer.get_token()
   1291                         line = read_full_line(lexer)
   1292                         if print_pkg_names:
   1293                                 continue
   1294                         try:
   1295                                 curpkg.chattr(fname, line)
   1296                         except Exception, e:
   1297                                 print "Can't change attributes on " + \
   1298                                     "'%s': not in the package" % fname, e
   1299                                 raise
   1300 
   1301                 elif token == "chattr_glob":
   1302                         glob = lexer.get_token()
   1303                         line = read_full_line(lexer)
   1304                         if print_pkg_names:
   1305                                 continue
   1306                         try:
   1307                                 curpkg.chattr_glob(glob, line)
   1308                         except Exception, e:
   1309                                 print "Can't change attributes on " + \
   1310                                     "'%s': no matches in the package" % \
   1311                                     glob, e
   1312                                 raise
   1313 
   1314                 elif in_multiline_import:
   1315                         next = lexer.get_token()
   1316                         if next == "with":
   1317                                 # I can't imagine this is supported, but there's
   1318                                 # no other way to read the rest of the line
   1319                                 # without a whole lot more pain.
   1320                                 line = read_full_line(lexer)
   1321                         else:
   1322                                 lexer.push_token(next)
   1323                                 line = ""
   1324 
   1325                         try:
   1326                                 curpkg.import_file(token, line)
   1327                         except Exception, e:
   1328                                 print "ERROR(import_file):", e
   1329                                 raise
   1330                 else:
   1331                         raise RuntimeError("Error: unknown token '%s' "
   1332                             "(%s:%s)" % (token, lexer.infile, lexer.lineno))
   1333 
   1334 if print_pkg_names:
   1335         for _mf in filelist:
   1336                 SolarisParse(_mf)
   1337         sys.exit(0)
   1338 
   1339 
   1340 print "First pass:", datetime.now()
   1341 
   1342 for _mf in filelist:
   1343         SolarisParse(_mf)
   1344 
   1345 seenpkgs = set(i[0] for i in usedlist.values())
   1346 
   1347 print "Files you seem to have forgotten:\n  " + "\n  ".join(
   1348     "%s %s" % (f.type, f.pathname)
   1349     for pkg in seenpkgs
   1350     for f in svr4pkgsseen[pkg].manifest
   1351     if f.type != "i" and f.pathname not in usedlist)
   1352 
   1353 print "\n\nDuplicate Editables files list:\n"
   1354 
   1355 if editable_files:
   1356         length = 2 + max(len(p) for p in editable_files)
   1357         for paths in editable_files:
   1358                 if len(editable_files[paths]) > 1:
   1359                         print ("%s:" % paths).ljust(length - 1) + \
   1360                             ("\n".ljust(length)).join("%s (from %s)" % \
   1361                             (l[1].name, l[0]) for l in editable_files[paths])
   1362 
   1363 
   1364 # Second pass: iterate over the existing package objects, gathering dependencies
   1365 # and publish!
   1366 
   1367 print "Second pass:", datetime.now()
   1368 
   1369 print "New packages:\n"
   1370 # XXX Sort these.  Preferably topologically, if possible, alphabetically
   1371 # otherwise (for a rough progress gauge).
   1372 if just_these_pkgs:
   1373         newpkgs = set(pkgdict[name]
   1374                       for name in pkgdict.keys()
   1375                       if name in just_these_pkgs
   1376                       )
   1377 else:
   1378         newpkgs = set(pkgdict.values())
   1379 
   1380 # Indicates whether search indices refresh will be deferred until the end.
   1381 defer_refresh = False
   1382 # Indicates whether local publishing is active.
   1383 local_publish = False
   1384 if def_repo.startswith("file:"):
   1385         # If publishing to disk, the search indices should be refreshed at
   1386         # the end of the publishing process and the feed cache will have to be
   1387         # generated by starting the depot server using the provided path and
   1388         # then accessing it.
   1389         defer_refresh = True
   1390         local_publish = True
   1391 
   1392 processed = 0
   1393 total = len(newpkgs)
   1394 for _p in sorted(newpkgs):
   1395         print "Package '%s'" % _p.name
   1396         print "  Version:", _p.version
   1397         print "  Description:", _p.desc
   1398         print "  Classification:", _p.classification
   1399         try:
   1400                 publish_pkg(_p)
   1401         except trans.TransactionError, _e:
   1402                 print "%s: FAILED: %s\n" % (_p.name, _e)
   1403         processed += 1
   1404         print "%d/%d packages processed; %.2f%% complete" % (processed, total,
   1405             processed * 100.0 / total)
   1406 
   1407 if not nopublish and defer_refresh:
   1408         # This has to be done at the end for some publishing modes.
   1409         print "Updating search indices..."
   1410         _t = trans.Transaction(def_repo)
   1411         _t.refresh_index()
   1412 
   1413 # Ensure that the feed is updated and cached to reflect changes.
   1414 if not nopublish:
   1415         print "Caching RSS/Atom feed..."
   1416         dc = None
   1417         durl = def_repo
   1418         if local_publish:
   1419                 # The depot server isn't already running, so will have to be
   1420                 # temporarily started to allow proper feed cache generation.
   1421                 dc = depotcontroller.DepotController()
   1422                 dc.set_depotd_path(g_proto_area + "/usr/lib/pkg.depotd")
   1423                 dc.set_depotd_content_root(g_proto_area + "/usr/share/lib/pkg")
   1424 
   1425                 _scheme, _netloc, _path, _params, _query, _fragment = \
   1426                     urlparse.urlparse(def_repo, "file", allow_fragments=0)
   1427 
   1428                 dc.set_repodir(_path)
   1429 
   1430                 # XXX There must be a better way...
   1431                 dc.set_port(29083)
   1432 
   1433                 # Start the depot
   1434                 dc.start()
   1435 
   1436                 durl = "http://localhost:29083"
   1437 
   1438         _f = urllib.urlopen("%s/feed" % durl)
   1439         _f.close()
   1440 
   1441         if dc:
   1442                 dc.stop()
   1443                 dc = None
   1444 
   1445 print "Done:", datetime.now()
   1446