Пример #1
0
    def find_packages_release(self, release):
        """effects: parse the release options specified to
        find_packages and pick up the packages from the base
        release"""
        myrelease = get_field(self.bin() + "/rootcore_config", "release")
        if myrelease:
            print "using release set with set_release"
            release = string.split(myrelease) + release

        rel_parser = optparse.OptionParser()
        WorkArea.init_release_options(rel_parser, False)
        (release_args, extra_release_args) = rel_parser.parse_args(release)
        if len(extra_release_args) != 0:
            rel_parser.error("incorrect number of arguments")

        WORKAREAREL = self.relPath(self.area(), self.bin())
        if os.path.isabs(WORKAREAREL):
            print "work area does not contain ROOTCOREBIN, aborting"
            print "  ROOTCOREBIN=" + self.bin()
            print "  WORKAREA=" + self.m_area
            print "  WORKAREAREL=" + WORKAREAREL
            sys.exit(1)

        self.m_packages = PackageList(self)
        for place in release_args.places:
            for pkg in WorkArea(RootCoreBin=place[0]).parsePackages():
                pkg.m_wa = self
                pkg.m_recompile = place[1]
                pkg.m_release = True
                self.m_packages.addPkg(pkg)
        self.m_pedantic = release_args.use_pedantic
        return release_args.restrict
Пример #2
0
    def find_packages_reorder(self, allowMiss, restrict):
        """effects: do the reordering of the packages based on
        dependencies for find_packages"""
        def addPkgDep(self, name, required, caller, cycle, catalog, allowMiss, wa):
            if self.getPkg(name):
                return
            for pkg in cycle:
                if name == pkg:
                    raise Exception("cyclical dependence involving packages: " +
                                    string.join(cycle))
            pkg = catalog.getPkg(name)
            if not pkg:
                if allowMiss or not required:
                    return
                message = ""
                if caller:
                    message = "package " + name + " not known, required by " + caller
                else:
                    message = "package " + name + " not known"
                shell_args = ["grep", "/" + name + "/tags", wa.dir() + "/all_packages"]
                known = shell_exec(shell_args, allowFail=True).split("\n")
                while len(known) > 0 and known[len(known) - 1] == "":
                    known.remove("")
                if len(known) > 0:
                    message += "\ntry checking it out with"
                    for pkg in known:
                        message += "\n  rc checkout_pkg " + pkg
                raise RCError(message)
            for dep in pkg.harddep():
                addPkgDep(self, dep, True, name, cycle + [name], catalog, allowMiss, wa)
            for dep in pkg.trydep():
                addPkgDep(self, dep, False, name, cycle + [name], catalog, allowMiss, wa)
            self.addPkg(pkg)

        ordered = PackageList(self)
        max_auto = 1
        while max_auto > 0:
            max_auto = 0
            for pkg in self.m_packages:
                if not ordered.getPkg(pkg.name()):
                    if pkg.auto() > max_auto:
                        max_auto = pkg.auto()
            if max_auto == 0:
                break
            for pkg in self.m_packages:
                if pkg.auto() == max_auto:
                    addPkgDep(ordered, pkg.name(), True, None, [],
                              self.m_packages, allowMiss, self)

        if len(restrict) > 0:
            for pkg in restrict:
                addPkgDep(ordered, pkg, True, None, [],
                          self.m_packages, allowMiss, self)
        else:
            for pkg in self.m_packages:
                addPkgDep(ordered, pkg.name(), True, None, [],
                          self.m_packages, allowMiss, self)
        self.m_packages = ordered
Пример #3
0
 def parsePackages(self):
     """returns: the list of all packages, as found by find_packages.
     if packages is specified it is used instead of the find_packages
     file."""
     path = os.path.join(self.bin(), "packages")
     if not os.path.isfile(path):
         raise RCError("could not find:\n  " + path +
                       "\nplease try running:\n  rc find_packages")
     result = PackageList(self)
     with open(self.bin() + "/packages") as file:
         for line in file:
             line = string.replace(line, "\n", "")
             if line != "" and line[0] != "#":
                 result.addPkg(PackageInfo.parsePackagesLine(line, self))
     return result
Пример #4
0
 def parsePackages(self):
     """returns: the list of all packages, as found by find_packages.
     if packages is specified it is used instead of the find_packages
     file."""
     path = os.path.join(self.bin(), "packages")
     if not os.path.isfile(path):
         raise RCError("could not find:\n  " + path +
                        "\nplease try running:\n  rc find_packages")
     result = PackageList(self)
     with open(self.bin() + "/packages") as file:
         for line in file:
             line = string.replace(line, "\n", "")
             if line != "" and line[0] != "#":
                 result.addPkg(PackageInfo.parsePackagesLine(line, self))
     return result
Пример #5
0
    def find_packages_release(self, release):
        """effects: parse the release options specified to
        find_packages and pick up the packages from the base
        release"""
        myrelease=get_field(self.bin() + "/rootcore_config", "release")
        if myrelease:
            print "using release set with set_release"
            release = string.split(myrelease) + release

        rel_parser = optparse.OptionParser()
        WorkArea.init_release_options(rel_parser, False)
        (release_args, extra_release_args) = rel_parser.parse_args(release)
        if len(extra_release_args) != 0:
            rel_parser.error("incorrect number of arguments")

        WORKAREAREL = self.relPath(self.area(), self.bin())
        if os.path.isabs(WORKAREAREL):
            print "work area does not contain ROOTCOREBIN, aborting"
            print "  ROOTCOREBIN=" + self.bin()
            print "  WORKAREA=" + self.m_area
            print "  WORKAREAREL=" + WORKAREAREL
            sys.exit(1)

        self.m_packages = PackageList(self)
        for place in release_args.places:
            for pkg in WorkArea(RootCoreBin=place[0]).parsePackages():
                pkg.m_wa = self
                pkg.m_recompile = place[1]
                pkg.m_release = True
                self.m_packages.addPkg(pkg)
        self.m_pedantic = release_args.use_pedantic
        return release_args.restrict
Пример #6
0
    def find_packages_reorder(self, allowMiss, restrict):
        """effects: do the reordering of the packages based on
        dependencies for find_packages"""
        def addPkgDep(self, name, required, caller, cycle, catalog, allowMiss,
                      wa):
            if self.getPkg(name):
                return
            for pkg in cycle:
                if name == pkg:
                    raise Exception(
                        "cyclical dependence involving packages: " +
                        string.join(cycle))
            pkg = catalog.getPkg(name)
            if not pkg:
                if allowMiss or not required:
                    return
                message = ""
                if caller:
                    message = "package " + name + " not known, required by " + caller
                else:
                    message = "package " + name + " not known"
                shell_args = [
                    "grep", "/" + name + "/tags",
                    wa.dir() + "/all_packages"
                ]
                known = shell_exec(shell_args, allowFail=True).split("\n")
                while len(known) > 0 and known[len(known) - 1] == "":
                    known.remove("")
                if len(known) > 0:
                    message += "\ntry checking it out with"
                    for pkg in known:
                        message += "\n  rc checkout_pkg " + pkg
                raise RCError(message)
            for dep in pkg.harddep():
                addPkgDep(self, dep, True, name, cycle + [name], catalog,
                          allowMiss, wa)
            for dep in pkg.trydep():
                addPkgDep(self, dep, False, name, cycle + [name], catalog,
                          allowMiss, wa)
            self.addPkg(pkg)

        ordered = PackageList(self)
        max_auto = 1
        while max_auto > 0:
            max_auto = 0
            for pkg in self.m_packages:
                if not ordered.getPkg(pkg.name()):
                    if pkg.auto() > max_auto:
                        max_auto = pkg.auto()
            if max_auto == 0:
                break
            for pkg in self.m_packages:
                if pkg.auto() == max_auto:
                    addPkgDep(ordered, pkg.name(), True, None, [],
                              self.m_packages, allowMiss, self)

        if len(restrict) > 0:
            for pkg in restrict:
                addPkgDep(ordered, pkg, True, None, [], self.m_packages,
                          allowMiss, self)
        else:
            for pkg in self.m_packages:
                addPkgDep(ordered, pkg.name(), True, None, [], self.m_packages,
                          allowMiss, self)
        self.m_packages = ordered
Пример #7
0
class WorkArea(object):
    """description: this class provides a joint interface for all the
    information about the work area"""
    def __init__(self, RootCoreBin=None, RootCoreDir=None):
        self.m_area = None
        self.m_areaList = None
        self.m_packages = None
        self.m_arch = os.getenv("ROOTCORECONFIG")
        if not self.m_arch:
            self.m_arch = "generic"
            pass
        self.m_root_conf = None
        self.m_pedantic = None

        if RootCoreDir and not os.path.isabs(RootCoreDir):
            RootCoreDir = os.path.join(os.getcwd(), RootCoreDir)
        if RootCoreBin and not os.path.isabs(RootCoreBin):
            RootCoreBin = os.path.join(os.getcwd(), RootCoreBin)

        if RootCoreDir != None:
            self.m_dir = RootCoreDir
            if RootCoreBin != None:
                self.m_bin = RootCoreBin
            else:
                self.m_bin = self.m_dir
            self.m_obj = os.path.join(self.m_bin, "obj")
        elif RootCoreBin != None:
            self.m_bin = RootCoreBin
            self.m_config = self.m_bin + "/rootcore_config"
            self.m_dir = self.expandPath(
                get_field(self.m_config, "rootcoredir"))
            if not self.m_dir or self.m_dir == "":
                self.m_dir = RootCoreBin
            self.m_obj = self.expandPath(
                get_field(self.m_config, "rootcoreobj"))
            if not self.m_obj or self.m_obj == "":
                self.m_obj = os.path.join(RootCoreBin, "obj")

            p = os.path
            if (not p.isfile(p.join(self.m_dir, "internal", "workarea.py"))
                    and not p.isfile(
                        p.join(self.m_dir, "internal", "RootCore.py"))):
                raise RCError("invalid RootCore installation: " + self.m_dir)

        else:
            self.m_dir = os.getenv("ROOTCOREDIR")
            self.m_bin = os.getenv("ROOTCOREBIN")
            self.m_obj = os.getenv("ROOTCOREOBJ")
            if not all([
                    self.m_dir,
                    os.path.isabs(self.m_dir), self.m_bin,
                    os.path.isabs(self.m_bin), self.m_obj,
                    os.path.isabs(self.m_obj)
            ]):
                raise RCError(
                    "environment not correctly setup please rerun RootCore setup"
                )
        self.m_config = self.m_bin + "/rootcore_config"

        if not self.m_dir or not os.path.isabs(self.m_dir):
            raise RCError("failed to configure ROOTCOREDIR")
        if not self.m_bin or not os.path.isabs(self.m_bin):
            raise RCError("failed to configure ROOTCOREBIN")
        if not self.m_obj or not os.path.isabs(self.m_obj):
            raise RCError("failed to configure ROOTCOREOBJ")
        pass

    # TODO: use property decoration for all of these
    def dir(self):
        """description: $ROOTCOREDIR, i.e. the location of the actual
        RootCore package"""
        return self.m_dir

    def bin(self):
        """description: $ROOTCOREBIN, i.e. the location where RootCore
        puts all the files it generates.  this can be the same as or
        different from $ROOTCOREBIN"""
        return self.m_bin

    def obj(self):
        """description: $ROOTCOREOBJ.
        rationale: In practice this is the same as $ROOTCOREBIN/obj.
        users can point this somewhere else if they are low on disk
        space for the area where $ROOTCOREBIN is located.  By
        construction all large files are located inside $ROOTCOREOBJ,
        not $ROOTCOREBIN.  However, given that afs space is getting
        more generous this option has become less necessary/useful,
        and I am unsure if it still works"""
        return self.m_obj

    def arch(self):
        """description: $ROOTCORECONFIG
        rationale: when used with the releases this will in general
        have the same format as CMTCONFIG, but outside of the releases
        the user has the option to override this with whatever he sees fit
        rationale: for RootCore itself this is nothing but an
        arbitrary string (although it tries to set reasonable
        defaults).  the only packages that actively care about the
        value of this variable are the externals packages"""
        return self.m_arch

    def config(self):
        """description: the location of the config file
        rationale: this file contains all global RootCore
        configuration, i.e. independent of $ROOTCORECONFIG and the
        packages used"""
        return self.m_config

    def libdir(self):
        """description: the location where the libraries get linked.
        actually they get linked in a subdirectory named after
        $ROOTCORECONFIG"""
        return os.path.join(self.bin(), "lib", self.arch())

    def bindir(self):
        """description: the location where the binaries get linked.
        actually they get linked in a subdirectory named after
        $ROOTCORECONFIG"""
        return os.path.join(self.bin(), "bin", self.arch())

    def pedantic(self):
        """description: 2 for compiling all packages pedantic, 1 for
        compiling the packages pedantic that request it, and 0 for
        compiling no packages pedantic"""
        if self.m_pedantic == None:
            pedantic = get_field(self.config(), "use_pedantic")
            if pedantic == "2":
                self.m_pedantic = 2
            elif pedantic == "0":
                self.m_pedantic = 0
            else:
                self.m_pedantic = 1
        return self.m_pedantic

    def root_version(self):
        """description: the root version description string.

        rationale: this is mostly used to check that we call 'rc
        clean' whenever we change root version"""
        if not hasattr(self, "m_root_version"):
            try:
                root_version = shell_exec(["root", "-b", "-q"])
                index = root_version.find("\nROOT")
                if index == -1:
                    raise RCError(
                        "failed to read root version, please send me the output of\nroot -b -q"
                    )
                root_version = root_version[index + 1:]
                index = root_version.find("\n")
                if index == -1:
                    raise RCError(
                        "failed to read root version, please send me the output of\nroot -b -q"
                    )
                self.m_root_version = root_version[0:index]
            except RCError:
                self.m_root_version = shell_exec(["root-config",
                                                  "--version"]).rstrip()
        return self.m_root_version

    def root_conf(self):
        """effects: determine the root parameters used for compilation
        returns: the path to the configuration file generated
        failures: incompatible root installation"""
        if not self.m_root_conf:
            file = os.path.join(self.bin(), "root_config_" + self.arch())
            ROOTSYS = os.getenv("ROOTSYS")
            if ROOTSYS == None:
                if os.path.isfile(file):
                    ROOTSYS = get_field(file, "ROOTSYS")
                    if ROOTSYS and ROOTSYS != "":
                        raise RCError(
                            "no valid root version found, try setting up root using\n"
                            + "  source " + ROOTSYS + "/bin/thisroot.sh")
                if os.path.isdir(
                        "/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase"):
                    raise RCError(
                        "no valid root version found, try setting up root using\n"
                        +
                        "  export ATLAS_LOCAL_ROOT_BASE=/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase\n"
                        +
                        "  source ${ATLAS_LOCAL_ROOT_BASE}/user/atlasLocalSetup.sh\n"
                        + "  localSetupROOT")
                if shell_exec(["which", "root-config"], allowFail=True) != "":
                    raise RCError(
                        "no valid root version found, try setting up root using\n"
                        + "  source `root-config --prefix`/bin/thisroot.sh")
                raise RCError(
                    "no valid root version found, please set up root")

            if os.path.isfile(file):
                myrootversion = get_field(file, "ROOT_VERSION")
                if not myrootversion:
                    set_field(file, "ROOT_VERSION", self.root_version())
                elif self.root_version() != myrootversion:
                    myrootsys = get_field(file, "ROOTSYS")
                    raise RCError(
                        "Root Version: " + self.root_version() + "\n" +
                        "is not the same as previous version:\n" + "   " +
                        myrootversion + "\n" +
                        "either set up the correct root version e.g. via:\n" +
                        "  source " + myrootsys + "/bin/thisroot.sh\n" +
                        "or clean out the old object files via:\n" +
                        "  rc clean")
                self.m_root_conf = file
                return file

            myarch = shell_exec(["root-config", "--etcdir"]) + "/Makefile.arch"
            if not os.path.isfile(myarch):
                myarch = os.getenv(
                    "ROOTSYS") + "/share/doc/root/test/Makefile.arch"
            if not os.path.isfile(myarch):
                shell_args = [
                    "find",
                    os.getenv("ROOTSYS") + "/.", "-name", "Makefile.arch"
                ]
                search = shell_exec(shell_args).strip().split("\n")
                if len(search) > 0:
                    myarch = search[0]
            if not os.path.isfile(myarch):
                raise RCError("failed to find Makefile.arch in " +
                              os.getenv("ROOTSYS"))

            with open(file + "-", "w") as f:
                shell_args = [
                    "make", "-f",
                    self.dir() + "/Makefile-print", "MAKEFILE_ARCH=" + myarch
                ]
                rc = shell_exec(shell_args, stdout=f, returnRC=True)
            if rc != 0:
                raise RCError("could not determine compilation parameters")
            os.rename(file + "-", file)
            self.m_root_conf = file
        return self.m_root_conf

    def getCxx(self, user=False):
        """returns: the c++ compiler to be used"""
        return get_field(self.root_conf(), "CXX")

    def getLd(self, user=False):
        """returns: the c++ linker to be used"""
        return get_field(self.root_conf(), "LD")

    # TODO: use a property instead
    def area(self):
        """description: the base directory of the work area, i.e. the
        directory in which find_packages was called"""
        if not self.m_area:
            rel = get_field(self.m_config, "workareaRelative", default="")
            if os.path.isabs(rel):
                raise RCError("workareaRelative set to invalid value '" + \
                              rel + "' in " + self.m_config)
            self.m_area = self.bin()
            if rel:
                while rel != "" and rel != ".":
                    self.m_area = os.path.dirname(self.m_area)
                    rel = os.path.dirname(rel)
        return self.m_area

    def areaList(self):
        """description: the base directory of the work area as a
        normalized list
        rationale: normalized lists are used when determining relative
        paths"""
        if not self.m_areaList:
            rel = RelPath(self.area())
            rel.resolve_full()
            self.m_areaList = rel
        return self.m_areaList

    def expandPath(self, path, area=None):
        """effects: expand a path found in the packages file
        returns: the expanded path
        rationale: this mostly takes care of converting between
        absolute paths and relative ones, so that the work area can be
        relocated"""
        if path == "" or path == None:
            return None
        if os.path.isabs(path):
            return path
        if not area:
            area = self.area()
        return os.path.join(area, path)

    def compressPath(self, path, area=None):
        """effects: compress a path for the packages file
        returns: the compressed path
        rationale: this mostly takes care of converting between
        absolute paths and relative ones, so that the work area can be
        relocated"""
        if not path or path == "":
            return ""
        if not area:
            area = self.area()
        return self.relPath(path, area, area)

    def relPath(self, file, dir=None, area=None):
        """effects: calculate the path of file relative to dir, if
        they are both in the work area, resolving symbolic links as
        necessary.  if dir is not specified it is assumed to be the
        working area
        returns: the relative path, which may have ".." inside, if
        they don't correspond to symbolic links.  or the original
        path, if this is not a relative path inside the workarea"""
        if area != None:
            areaList = RelPath(area)
            areaList.resolve_full()
        else:
            areaList = self.areaList()
        fileList = RelPath(file)
        if not fileList.resolve_area(areaList):
            return file
        if dir:
            dirList = RelPath(dir)
            if not dirList.resolve_area(areaList):
                return file
        else:
            dirList = copy.deepcopy(areaList)

        if not dirList.resolve_relative(fileList):
            return file
        return dirList.m_relative

    def lnRel(self, source, target):
        """effects: link source to target, making a relative link if
        that link would not leave the work area.  if the link already
        exists, nothing is done"""
        rel = self.relPath(source, os.path.dirname(target))
        if os.path.islink(target):
            link = os.readlink(target)
            if link == rel:
                return
            os.remove(target)
        elif os.path.exists(target):
            # raise RCError("can not replace non-link with link: " + target)
            os.remove(target)
        mkdir_p(os.path.dirname(target))
        os.symlink(rel, target)

    def isMac(self):
        """returns: whether this is a mac architecture
        rationale: this is used in various places in the build
        procedure where MacOS behaves differently from Linux"""
        if not hasattr(self, "m_isMac"):
            if self.m_arch.find("-mac") != -1:
                self.m_isMac = True
            elif self.m_arch.find("-slc") != -1:
                self.m_isMac = False
            else:
                self.m_isMac = (shell_exec(["uname"]) == "Darwin")
        return self.m_isMac

    def hasPackages(self):
        """returns: whether we already have a packages file,
        i.e. whether find_packages has already been called"""
        return self.m_packages or os.path.exists(
            os.path.join(self.bin(), "packages"))

    def packages(self):
        """description: the list of packages we are using"""
        if not self.m_packages:
            self.m_packages = self.parsePackages()
        return self.m_packages

    def parsePackages(self):
        """returns: the list of all packages, as found by find_packages.
        if packages is specified it is used instead of the find_packages
        file."""
        path = os.path.join(self.bin(), "packages")
        if not os.path.isfile(path):
            raise RCError("could not find:\n  " + path +
                          "\nplease try running:\n  rc find_packages")
        result = PackageList(self)
        with open(self.bin() + "/packages") as file:
            for line in file:
                line = string.replace(line, "\n", "")
                if line != "" and line[0] != "#":
                    result.addPkg(PackageInfo.parsePackagesLine(line, self))
        return result

    def writePackages(self, packages):
        """effects: writes the content of the package list into a new
        package file"""
        with open(self.bin() + "/packages", "w") as file:
            for pkg in packages:
                pkg.writePackagesLine(file)

    def write_config(self):
        """effects: write the configuration for the workarea as it is
        configured now"""
        if not os.path.isfile(self.config()):
            open(self.config(), "w").close()
        set_field(self.config(), "workareaRelative",
                  self.relPath(self.area(), self.bin()))
        set_field(self.config(), "rootcoredir", self.compressPath(self.dir()))
        set_field(self.config(), "rootcoreobj", self.compressPath(self.obj()))
        set_field(self.config(), "rootcoreconf", self.arch())
        self.lnRel(os.path.join(self.dir(), "internal", "local_setup.sh"),
                   os.path.join(self.bin(), "local_setup.sh"))
        self.lnRel(os.path.join(self.dir(), "internal", "local_setup.csh"),
                   os.path.join(self.bin(), "local_setup.csh"))

    def ln_rc(self):
        """effects: create the link for rc"""
        source = os.path.join(self.dir(), "internal", "rc")
        target = os.path.join(self.bin(), self.bin(), "bin", self.arch(), "rc")
        self.lnRel(source, target)

    def ut_test_list(self, package, recursive):
        """returns: the list of tests we have"""
        result = []
        if package:
            pkg = self.packages().getPkg(package)
            if not pkg:
                raise RCError("unknown package: " + package)
            packages = [pkg]
            pass
        else:
            packages = self.packages()
            pass
        for pkg in packages:
            result += pkg.ut_test_list(recursive)
            pass
        return result

    # NOTE: might be best to keep the option parsing in with
    # the top-level commands. Is it possible?
    @classmethod
    def init_release_options(cls, parser, accu):
        """effects: initialize the release options for the
        OptionParser, i.e. all the options that actually select the
        release as passed into find_packages and set_release"""
        def release_option(option, opt_str, value, parser):
            release = getattr(parser.values, option.dest)
            release.append(opt_str)
            if value != None:
                release += [value]

        def base_release_option(option, opt_str, value, parser, args):
            getattr(parser.values,
                    option.dest).append([value, option.callback_args[0]])

        if accu:
            parser.add_option("--src-release",
                              dest="release",
                              default=[],
                              type="string",
                              action="callback",
                              callback=release_option)
            parser.add_option("--obj-release",
                              dest="release",
                              default=[],
                              type="string",
                              action="callback",
                              callback=release_option)
            parser.add_option("--restrict",
                              dest="release",
                              default=[],
                              type="string",
                              action="callback",
                              callback=release_option)
            parser.add_option("--drop-restrict",
                              dest="release",
                              default=[],
                              action="callback",
                              callback=release_option)
            parser.add_option("--no-pedantic",
                              dest="release",
                              default=[],
                              type="string",
                              action="callback",
                              callback=release_option)
            parser.add_option("--force-pedantic",
                              dest="release",
                              default=[],
                              type="string",
                              action="callback",
                              callback=release_option)
        else:
            parser.add_option("--src-release",
                              dest="places",
                              default=[],
                              type="string",
                              action="callback",
                              callback=base_release_option,
                              callback_args=tuple([True]))
            parser.add_option("--obj-release",
                              dest="places",
                              default=[],
                              type="string",
                              action="callback",
                              callback=base_release_option,
                              callback_args=tuple([False]))
            parser.add_option("--restrict",
                              dest="restrict",
                              default=[],
                              action="append")
            parser.add_option("--drop-restrict",
                              dest="restrict",
                              default=[],
                              action="store_const",
                              const=[])
            parser.add_option("--no-pedantic",
                              dest="use_pedantic",
                              default=1,
                              action="store_const",
                              const=0)
            parser.add_option("--force-pedantic",
                              dest="use_pedantic",
                              default=1,
                              action="store_const",
                              const=2)

    def find_packages_release(self, release):
        """effects: parse the release options specified to
        find_packages and pick up the packages from the base
        release"""
        myrelease = get_field(self.bin() + "/rootcore_config", "release")
        if myrelease:
            print "using release set with set_release"
            release = string.split(myrelease) + release

        rel_parser = optparse.OptionParser()
        WorkArea.init_release_options(rel_parser, False)
        (release_args, extra_release_args) = rel_parser.parse_args(release)
        if len(extra_release_args) != 0:
            rel_parser.error("incorrect number of arguments")

        WORKAREAREL = self.relPath(self.area(), self.bin())
        if os.path.isabs(WORKAREAREL):
            print "work area does not contain ROOTCOREBIN, aborting"
            print "  ROOTCOREBIN=" + self.bin()
            print "  WORKAREA=" + self.m_area
            print "  WORKAREAREL=" + WORKAREAREL
            sys.exit(1)

        self.m_packages = PackageList(self)
        for place in release_args.places:
            for pkg in WorkArea(RootCoreBin=place[0]).parsePackages():
                pkg.m_wa = self
                pkg.m_recompile = place[1]
                pkg.m_release = True
                self.m_packages.addPkg(pkg)
        self.m_pedantic = release_args.use_pedantic
        return release_args.restrict

    def find_packages_find(self, area, allowNesting):
        """effects: search for packages in the work area"""
        for filename in os.listdir(area):
            dir = os.path.join(area, filename)
            is_package = False
            if os.path.exists(os.path.join(dir, "cmt", "Makefile.RootCore")):
                is_package = True
                name = os.path.basename(dir)
                name2 = get_field(dir + "/cmt/Makefile.RootCore", "PACKAGE")
                if not name2:
                    raise Exception("failed to read package name from " + dir +
                                    "/cmt/Makefile.RootCore")
                if name != name2:
                    #raise Exception("package " + dir + " should have name " + name2 + "\n" +
                    #                "please rename it to " + os.path.dirname(dir) + "/" + name2 + " by typing\n" +
                    #                "  mv '" + dir + "' '" + os.path.dirname(dir) + "/" + name2 + "'")
                    dirname = os.path.dirname(dir) + "/" + name2
                    exstr = "package %s should have name %s\n" % (dir, name2)
                    exstr += "please rename it to %s by typing\n" % dirname
                    exstr += "  mv '%s' '%s'" % (dir, dirname)
                    raise Exception(exstr)
                pkg2 = self.m_packages.getPkg(name)
                if pkg2 and pkg2.release() == 0:
                    raise Exception(
                        "duplicate packages, please remove one of them:\n" +
                        "  " + dir + "\n" + "  " + pkg2.srcdir())

                pkg = PackageInfo(self)
                pkg.m_name = name
                pkg.m_srcdir = dir
                pkg.m_release = False
                pkg.m_recompile = True
                self.m_packages.addPkg(pkg)
            if ((not is_package or allowNesting) and os.path.isdir(dir) and
                (area != self.bin() or
                 (filename != "RootCore" and filename != "bin"
                  and filename != "data" and filename != "include"
                  and filename != "lib" and filename != "obj"
                  and filename != "python" and filename != "user_scripts"))):
                self.find_packages_find(dir, allowNesting)

    def find_packages_reorder(self, allowMiss, restrict):
        """effects: do the reordering of the packages based on
        dependencies for find_packages"""
        def addPkgDep(self, name, required, caller, cycle, catalog, allowMiss,
                      wa):
            if self.getPkg(name):
                return
            for pkg in cycle:
                if name == pkg:
                    raise Exception(
                        "cyclical dependence involving packages: " +
                        string.join(cycle))
            pkg = catalog.getPkg(name)
            if not pkg:
                if allowMiss or not required:
                    return
                message = ""
                if caller:
                    message = "package " + name + " not known, required by " + caller
                else:
                    message = "package " + name + " not known"
                shell_args = [
                    "grep", "/" + name + "/tags",
                    wa.dir() + "/all_packages"
                ]
                known = shell_exec(shell_args, allowFail=True).split("\n")
                while len(known) > 0 and known[len(known) - 1] == "":
                    known.remove("")
                if len(known) > 0:
                    message += "\ntry checking it out with"
                    for pkg in known:
                        message += "\n  rc checkout_pkg " + pkg
                raise RCError(message)
            for dep in pkg.harddep():
                addPkgDep(self, dep, True, name, cycle + [name], catalog,
                          allowMiss, wa)
            for dep in pkg.trydep():
                addPkgDep(self, dep, False, name, cycle + [name], catalog,
                          allowMiss, wa)
            self.addPkg(pkg)

        ordered = PackageList(self)
        max_auto = 1
        while max_auto > 0:
            max_auto = 0
            for pkg in self.m_packages:
                if not ordered.getPkg(pkg.name()):
                    if pkg.auto() > max_auto:
                        max_auto = pkg.auto()
            if max_auto == 0:
                break
            for pkg in self.m_packages:
                if pkg.auto() == max_auto:
                    addPkgDep(ordered, pkg.name(), True, None, [],
                              self.m_packages, allowMiss, self)

        if len(restrict) > 0:
            for pkg in restrict:
                addPkgDep(ordered, pkg, True, None, [], self.m_packages,
                          allowMiss, self)
        else:
            for pkg in self.m_packages:
                addPkgDep(ordered, pkg.name(), True, None, [], self.m_packages,
                          allowMiss, self)
        self.m_packages = ordered

    def find_packages_dep_rebuild(self):
        """effects: set the determined dependencies and the rebuild
        flags for the find_packages operation"""
        myauto = []
        for pkg in self.m_packages:
            pkg.m_dep = []
            for dep in myauto:
                pkg.m_dep.append(dep)
            for dep in pkg.harddep() + pkg.trydep():
                pkg2 = self.m_packages.getPkg(dep)
                if pkg2:
                    for mydep in pkg2.dep():
                        if not mydep in pkg.m_dep:
                            pkg.m_dep.append(mydep)
                    if pkg2.recompile():
                        pkg.m_recompile = True
            pkg.m_dep.append(pkg.name())
            if pkg.auto() > 0 and pkg.name() != "RootCore":
                for dep in pkg.m_dep:
                    if not dep in myauto:
                        myauto = myauto + [dep]
            if pkg.name() == "RootCore" or pkg.recompile() == True:
                pkg.m_outdir = self.obj()

    def find_packages_write(self):
        """effects: write out the result of the find_packages
        operation"""
        RootCore = self.m_packages.getPkg("RootCore")
        if RootCore.srcdir() != self.dir():
            print "WARNING: RootCore installation changed, please setup again in a new shell"
            self.m_dir = RootCore.srcdir()

        CXXFLAGS = ""
        LDFLAGS = ""
        if os.getenv("CXXFLAGS"):
            CXXFLAGS = os.getenv("CXXFLAGS")
        if os.getenv("LDFLAGS"):
            LDFLAGS = os.getenv("LDFLAGS")
        if os.getenv("EXTRA_CFLAGS"):
            CXXFLAGS += " " + os.getenv("EXTRA_CFLAGS")
        if os.getenv("CPPEXFLAGS"):
            CXXFLAGS += " " + os.getenv("CPPEXFLAGS")
            LDFLAGS += " " + os.getenv("CPPEXFLAGS")
        set_field(self.config(), "CXXFLAGS", CXXFLAGS)
        set_field(self.config(), "LDFLAGS", LDFLAGS)
        set_field(self.config(), "use_pedantic", str(self.m_pedantic))
        self.write_config()
        self.writePackages(self.m_packages)

    def find_packages(self, release, allowMiss, allowNesting):
        """effects: perform the find_packages command"""

        restrict = self.find_packages_release(release)

        print "looking for packages in " + self.m_area
        self.find_packages_find(self.m_area, allowNesting)

        self.m_packages.sortByName()
        print
        print "packages found:"
        for pkg in self.m_packages:
            print pkg.srcdir()

        self.find_packages_reorder(allowMiss, restrict)

        print
        print "sorted packages:"
        for pkg in self.m_packages:
            print pkg.srcdir()
        self.find_packages_dep_rebuild()
        # rationale: I am setting the recompile flag for RootCore,
        # because it's outdir always points to the current output
        # directory, even if it comes from the release.  I'm setting
        # this here, to avoid accidentally recompiling everything
        # depending on RootCore.
        self.m_packages.getPkg("RootCore").m_recompile = True
        print
        print "writing changes"
        self.find_packages_write()
        print "done"

    def compileCore(self, packages, log_files, cont):
        """effects: do the core part of 'rc compile', i.e. compile all
        the packages"""

        failures = []
        for pkg in packages:
            log_file = None
            if log_files:
                path = pkg.nicos_name() + ".loglog"
                logdir = os.getenv("ROOTCORELOG")
                if logdir:
                    path = os.path.join(logdir, path)
                    pass
                print "writing " + pkg.name() + " compilation log to " + path
                log_file = open(path, "w")
            try:
                if not pkg.compile(ignoreErrors=cont, logFile=log_file):
                    raise RCError("failed to compile : " + pkg.name())
            except Exception as e:
                failures.append(pkg.name())
                if not cont:
                    raise
            finally:
                if log_file:
                    log_file.close()
                    pass
                pass
            pass

        if len(failures) > 0:
            raise RCError("compilation failures in: " +
                          string.join(failures, " "))
        pass

    pass
Пример #8
0
class WorkArea(object) :
    """description: this class provides a joint interface for all the
    information about the work area"""

    def __init__(self, RootCoreBin=None, RootCoreDir=None):
        self.m_area = None
        self.m_areaList = None
        self.m_packages = None
        self.m_arch = os.getenv("ROOTCORECONFIG")
        if not self.m_arch:
            self.m_arch = "generic"
            pass
        self.m_root_conf = None
        self.m_pedantic = None

        if RootCoreDir and not os.path.isabs(RootCoreDir):
            RootCoreDir = os.path.join(os.getcwd(), RootCoreDir)
        if RootCoreBin and not os.path.isabs(RootCoreBin):
            RootCoreBin = os.path.join(os.getcwd(), RootCoreBin)

        if RootCoreDir != None:
            self.m_dir = RootCoreDir
            if RootCoreBin != None:
                self.m_bin = RootCoreBin
            else:
                self.m_bin = self.m_dir
            self.m_obj = os.path.join(self.m_bin, "obj")
        elif RootCoreBin != None:
            self.m_bin = RootCoreBin
            self.m_config = self.m_bin + "/rootcore_config"
            self.m_dir = self.expandPath(get_field(self.m_config, "rootcoredir"))
            if not self.m_dir or self.m_dir == "":
                self.m_dir = RootCoreBin
            self.m_obj = self.expandPath(get_field(self.m_config, "rootcoreobj"))
            if not self.m_obj or self.m_obj == "":
                self.m_obj = os.path.join(RootCoreBin, "obj")

            p = os.path
            if (not p.isfile(p.join(self.m_dir, "internal", "workarea.py")) and
                not p.isfile(p.join(self.m_dir, "internal", "RootCore.py"))):
                raise RCError("invalid RootCore installation: " + self.m_dir)
        
        else:
            self.m_dir = os.getenv("ROOTCOREDIR")
            self.m_bin = os.getenv("ROOTCOREBIN")
            self.m_obj = os.getenv("ROOTCOREOBJ")
            if not all([self.m_dir, os.path.isabs(self.m_dir),
                        self.m_bin, os.path.isabs(self.m_bin),
                        self.m_obj, os.path.isabs(self.m_obj)]):
                raise RCError("environment not correctly setup please rerun RootCore setup")
        self.m_config = self.m_bin + "/rootcore_config"

        if not self.m_dir or not os.path.isabs(self.m_dir):
            raise RCError("failed to configure ROOTCOREDIR")
        if not self.m_bin or not os.path.isabs(self.m_bin):
            raise RCError("failed to configure ROOTCOREBIN")
        if not self.m_obj or not os.path.isabs(self.m_obj):
            raise RCError("failed to configure ROOTCOREOBJ")
        pass

    # TODO: use property decoration for all of these
    def dir(self):
        """description: $ROOTCOREDIR, i.e. the location of the actual
        RootCore package"""
        return self.m_dir

    def bin(self):
        """description: $ROOTCOREBIN, i.e. the location where RootCore
        puts all the files it generates.  this can be the same as or
        different from $ROOTCOREBIN"""
        return self.m_bin

    def obj(self):
        """description: $ROOTCOREOBJ.
        rationale: In practice this is the same as $ROOTCOREBIN/obj.
        users can point this somewhere else if they are low on disk
        space for the area where $ROOTCOREBIN is located.  By
        construction all large files are located inside $ROOTCOREOBJ,
        not $ROOTCOREBIN.  However, given that afs space is getting
        more generous this option has become less necessary/useful,
        and I am unsure if it still works"""
        return self.m_obj

    def arch(self):
        """description: $ROOTCORECONFIG
        rationale: when used with the releases this will in general
        have the same format as CMTCONFIG, but outside of the releases
        the user has the option to override this with whatever he sees fit
        rationale: for RootCore itself this is nothing but an
        arbitrary string (although it tries to set reasonable
        defaults).  the only packages that actively care about the
        value of this variable are the externals packages"""
        return self.m_arch

    def config(self):
        """description: the location of the config file
        rationale: this file contains all global RootCore
        configuration, i.e. independent of $ROOTCORECONFIG and the
        packages used"""
        return self.m_config

    def libdir(self) :
        """description: the location where the libraries get linked.
        actually they get linked in a subdirectory named after
        $ROOTCORECONFIG"""
        return os.path.join(self.bin(), "lib", self.arch())

    def bindir(self) :
        """description: the location where the binaries get linked.
        actually they get linked in a subdirectory named after
        $ROOTCORECONFIG"""
        return os.path.join(self.bin(), "bin", self.arch())

    def pedantic(self):
        """description: 2 for compiling all packages pedantic, 1 for
        compiling the packages pedantic that request it, and 0 for
        compiling no packages pedantic"""
        if self.m_pedantic == None:
            pedantic = get_field(self.config(), "use_pedantic")
            if pedantic == "2":
                self.m_pedantic = 2
            elif pedantic == "0":
                self.m_pedantic = 0
            else:
                self.m_pedantic = 1
        return self.m_pedantic

    def root_version(self):
        """description: the root version description string.

        rationale: this is mostly used to check that we call 'rc
        clean' whenever we change root version"""
        if not hasattr(self, "m_root_version"):
            try:
                root_version = shell_exec(["root", "-b", "-q"])
                index = root_version.find("\nROOT")
                if index == -1:
                    raise RCError("failed to read root version, please send me the output of\nroot -b -q")
                root_version = root_version[index+1:]
                index = root_version.find("\n")
                if index == -1:
                    raise RCError("failed to read root version, please send me the output of\nroot -b -q")
                self.m_root_version = root_version[0:index]
            except RCError:
                self.m_root_version = shell_exec(["root-config", "--version"]).rstrip()
        return self.m_root_version

    def root_conf(self):
        """effects: determine the root parameters used for compilation
        returns: the path to the configuration file generated
        failures: incompatible root installation"""
        if not self.m_root_conf:
            file = os.path.join(self.bin(), "root_config_" + self.arch())
            ROOTSYS = os.getenv("ROOTSYS")
            if ROOTSYS == None:
                if os.path.isfile(file):
                    ROOTSYS = get_field(file, "ROOTSYS")
                    if ROOTSYS and ROOTSYS != "":
                        raise RCError("no valid root version found, try setting up root using\n" +
                                      "  source " + ROOTSYS + "/bin/thisroot.sh")
                if os.path.isdir("/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase"):
                    raise RCError("no valid root version found, try setting up root using\n" +
                                  "  export ATLAS_LOCAL_ROOT_BASE=/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase\n" +
                                  "  source ${ATLAS_LOCAL_ROOT_BASE}/user/atlasLocalSetup.sh\n" +
                                  "  localSetupROOT")
                if shell_exec(["which", "root-config"], allowFail=True) != "":
                    raise RCError("no valid root version found, try setting up root using\n" +
                                  "  source `root-config --prefix`/bin/thisroot.sh")
                raise RCError("no valid root version found, please set up root")

            if os.path.isfile(file):
                myrootversion = get_field(file, "ROOT_VERSION")
                if not myrootversion:
                    set_field(file, "ROOT_VERSION", self.root_version())
                elif self.root_version() != myrootversion:
                    myrootsys = get_field(file, "ROOTSYS")
                    raise RCError("Root Version: " + self.root_version() + "\n" +
                                  "is not the same as previous version:\n" +
                                  "   " + myrootversion + "\n" +
                                  "either set up the correct root version e.g. via:\n" +
                                  "  source " + myrootsys + "/bin/thisroot.sh\n" +
                                  "or clean out the old object files via:\n" +
                                  "  rc clean")
                self.m_root_conf = file
                return file

            myarch = shell_exec(["root-config", "--etcdir"]) + "/Makefile.arch"
            if not os.path.isfile(myarch):
                myarch = os.getenv("ROOTSYS") + "/share/doc/root/test/Makefile.arch"
            if not os.path.isfile(myarch):
                shell_args = ["find", os.getenv("ROOTSYS") + "/.",
                              "-name", "Makefile.arch"]
                search = shell_exec(shell_args).strip().split("\n")
                if len(search) > 0:
                    myarch = search[0]
            if not os.path.isfile(myarch):
                raise RCError("failed to find Makefile.arch in " + os.getenv("ROOTSYS"))

            with open(file + "-", "w") as f:
                shell_args = ["make", "-f", self.dir() + "/Makefile-print",
                              "MAKEFILE_ARCH=" + myarch]
                rc = shell_exec(shell_args, stdout=f, returnRC=True)
            if rc != 0:
                raise RCError("could not determine compilation parameters")
            os.rename(file + "-", file)
            self.m_root_conf = file
        return self.m_root_conf

    def getCxx(self, user=False):
        """returns: the c++ compiler to be used"""
        return get_field(self.root_conf(), "CXX")

    def getLd(self, user=False):
        """returns: the c++ linker to be used"""
        return get_field(self.root_conf(), "LD")


    # TODO: use a property instead
    def area(self):
        """description: the base directory of the work area, i.e. the
        directory in which find_packages was called"""
        if not self.m_area:
            rel = get_field(self.m_config, "workareaRelative", default="")
            if os.path.isabs(rel):
                raise RCError("workareaRelative set to invalid value '" + \
                              rel + "' in " + self.m_config)
            self.m_area = self.bin()
            if rel:
                while rel != "" and rel != ".":
                    self.m_area = os.path.dirname(self.m_area)
                    rel = os.path.dirname(rel)
        return self.m_area

    def areaList(self):
        """description: the base directory of the work area as a
        normalized list
        rationale: normalized lists are used when determining relative
        paths"""
        if not self.m_areaList:
            rel = RelPath(self.area())
            rel.resolve_full()
            self.m_areaList = rel
        return self.m_areaList

    def expandPath(self, path, area=None) :
        """effects: expand a path found in the packages file
        returns: the expanded path
        rationale: this mostly takes care of converting between
        absolute paths and relative ones, so that the work area can be
        relocated"""
        if path == "" or path == None:
            return None
        if os.path.isabs(path) :
            return path
        if not area:
            area = self.area()
        return os.path.join(area, path)

    def compressPath(self, path, area=None) :
        """effects: compress a path for the packages file
        returns: the compressed path
        rationale: this mostly takes care of converting between
        absolute paths and relative ones, so that the work area can be
        relocated"""
        if not path or path == "":
            return ""
        if not area:
            area = self.area()
        return self.relPath(path, area, area)

    def relPath(self, file, dir = None, area = None) :
        """effects: calculate the path of file relative to dir, if
        they are both in the work area, resolving symbolic links as
        necessary.  if dir is not specified it is assumed to be the
        working area
        returns: the relative path, which may have ".." inside, if
        they don't correspond to symbolic links.  or the original
        path, if this is not a relative path inside the workarea"""
        if area != None:
            areaList = RelPath(area)
            areaList.resolve_full()
        else:
            areaList = self.areaList()
        fileList = RelPath(file)
        if not fileList.resolve_area(areaList):
            return file
        if dir:
            dirList = RelPath(dir)
            if not dirList.resolve_area(areaList):
                return file
        else:
            dirList = copy.deepcopy(areaList)

        if not dirList.resolve_relative(fileList):
            return file
        return dirList.m_relative

    def lnRel(self, source, target) :
        """effects: link source to target, making a relative link if
        that link would not leave the work area.  if the link already
        exists, nothing is done"""
        rel = self.relPath(source, os.path.dirname(target))
        if os.path.islink(target):
            link = os.readlink(target)
            if link == rel:
                return
            os.remove(target)
        elif os.path.exists(target):
            # raise RCError("can not replace non-link with link: " + target)
            os.remove(target)
        mkdir_p(os.path.dirname(target))
        os.symlink(rel, target)

    def isMac(self) :
        """returns: whether this is a mac architecture
        rationale: this is used in various places in the build
        procedure where MacOS behaves differently from Linux"""
        if not hasattr(self, "m_isMac") :
            if self.m_arch.find("-mac") != -1 :
                self.m_isMac = True
            elif self.m_arch.find("-slc") != -1 :
                self.m_isMac = False
            else :
                self.m_isMac =(shell_exec(["uname"]) == "Darwin")
        return self.m_isMac

    def hasPackages(self) :
        """returns: whether we already have a packages file,
        i.e. whether find_packages has already been called"""
        return self.m_packages or os.path.exists(os.path.join(self.bin(),
                                                              "packages"))

    def packages(self):
        """description: the list of packages we are using"""
        if not self.m_packages:
            self.m_packages = self.parsePackages()
        return self.m_packages

    def parsePackages(self):
        """returns: the list of all packages, as found by find_packages.
        if packages is specified it is used instead of the find_packages
        file."""
        path = os.path.join(self.bin(), "packages")
        if not os.path.isfile(path):
            raise RCError("could not find:\n  " + path +
                           "\nplease try running:\n  rc find_packages")
        result = PackageList(self)
        with open(self.bin() + "/packages") as file:
            for line in file:
                line = string.replace(line, "\n", "")
                if line != "" and line[0] != "#":
                    result.addPkg(PackageInfo.parsePackagesLine(line, self))
        return result

    def writePackages(self, packages):
        """effects: writes the content of the package list into a new
        package file"""
        with open(self.bin() + "/packages", "w") as file:
            for pkg in packages:
                pkg.writePackagesLine(file)

    def write_config(self) :
        """effects: write the configuration for the workarea as it is
        configured now"""
        if not os.path.isfile(self.config()):
            open(self.config(), "w").close()
        set_field(self.config(), "workareaRelative",
                  self.relPath(self.area(), self.bin()))
        set_field(self.config(), "rootcoredir", self.compressPath(self.dir()))
        set_field(self.config(), "rootcoreobj", self.compressPath(self.obj()))
        set_field(self.config(), "rootcoreconf", self.arch())
        self.lnRel(os.path.join(self.dir(), "internal", "local_setup.sh"),
                   os.path.join(self.bin(), "local_setup.sh"))
        self.lnRel(os.path.join(self.dir(), "internal", "local_setup.csh"),
                   os.path.join(self.bin(), "local_setup.csh"))

    def ln_rc(self) :
        """effects: create the link for rc"""
        source = os.path.join(self.dir(), "internal", "rc")
        target = os.path.join(self.bin(), self.bin(), "bin", self.arch(), "rc")
        self.lnRel(source, target)

    def ut_test_list(self,package,recursive):
        """returns: the list of tests we have"""
        result = []
        if package :
            pkg = self.packages().getPkg(package)
            if not pkg :
                raise RCError ("unknown package: " + package)
            packages = [pkg]
            pass
        else :
            packages = self.packages()
            pass
        for pkg in packages :
            result += pkg.ut_test_list (recursive)
            pass
        return result

    # NOTE: might be best to keep the option parsing in with
    # the top-level commands. Is it possible?
    @classmethod
    def init_release_options(cls, parser, accu):
        """effects: initialize the release options for the
        OptionParser, i.e. all the options that actually select the
        release as passed into find_packages and set_release"""

        def release_option(option, opt_str, value, parser):
            release = getattr(parser.values, option.dest)
            release.append(opt_str)
            if value != None:
                release += [value]
        def base_release_option(option, opt_str, value, parser, args):
            getattr(parser.values, option.dest).append([value, option.callback_args[0]])
        if accu:
            parser.add_option("--src-release", dest="release", default=[],
                              type="string", action="callback",
                              callback=release_option)
            parser.add_option("--obj-release", dest="release",default=[],
                              type="string", action="callback",
                              callback=release_option)
            parser.add_option("--restrict", dest="release", default=[],
                              type="string", action="callback",
                              callback=release_option)
            parser.add_option("--drop-restrict", dest="release", default=[],
                              action="callback", callback=release_option)
            parser.add_option("--no-pedantic", dest="release", default=[],
                              type="string", action="callback",
                              callback=release_option)
            parser.add_option("--force-pedantic", dest="release", default=[],
                              type="string", action="callback",
                              callback=release_option)
        else:
            parser.add_option("--src-release", dest="places", default=[],
                              type="string", action="callback",
                              callback=base_release_option,
                              callback_args=tuple([True]))
            parser.add_option("--obj-release", dest="places",default=[],
                              type="string", action="callback",
                              callback=base_release_option,
                              callback_args=tuple([False]))
            parser.add_option("--restrict", dest="restrict", default=[],
                              action="append")
            parser.add_option("--drop-restrict", dest="restrict", default=[],
                              action="store_const", const=[])
            parser.add_option("--no-pedantic", dest="use_pedantic", default=1,
                              action="store_const", const=0)
            parser.add_option("--force-pedantic", dest="use_pedantic",
                              default=1, action="store_const", const=2)

    def find_packages_release(self, release):
        """effects: parse the release options specified to
        find_packages and pick up the packages from the base
        release"""
        myrelease=get_field(self.bin() + "/rootcore_config", "release")
        if myrelease:
            print "using release set with set_release"
            release = string.split(myrelease) + release

        rel_parser = optparse.OptionParser()
        WorkArea.init_release_options(rel_parser, False)
        (release_args, extra_release_args) = rel_parser.parse_args(release)
        if len(extra_release_args) != 0:
            rel_parser.error("incorrect number of arguments")

        WORKAREAREL = self.relPath(self.area(), self.bin())
        if os.path.isabs(WORKAREAREL):
            print "work area does not contain ROOTCOREBIN, aborting"
            print "  ROOTCOREBIN=" + self.bin()
            print "  WORKAREA=" + self.m_area
            print "  WORKAREAREL=" + WORKAREAREL
            sys.exit(1)

        self.m_packages = PackageList(self)
        for place in release_args.places:
            for pkg in WorkArea(RootCoreBin=place[0]).parsePackages():
                pkg.m_wa = self
                pkg.m_recompile = place[1]
                pkg.m_release = True
                self.m_packages.addPkg(pkg)
        self.m_pedantic = release_args.use_pedantic
        return release_args.restrict

    def find_packages_find(self, area, allowNesting):
        """effects: search for packages in the work area"""
        for filename in os.listdir(area):
            dir = os.path.join(area, filename)
            is_package = False
            if os.path.exists(os.path.join(dir, "cmt", "Makefile.RootCore")):
                is_package = True
                name=os.path.basename(dir)
                name2=get_field(dir + "/cmt/Makefile.RootCore", "PACKAGE")
                if not name2:
                    raise Exception("failed to read package name from " +
                                    dir + "/cmt/Makefile.RootCore")
                if name != name2:
                    #raise Exception("package " + dir + " should have name " + name2 + "\n" +
                    #                "please rename it to " + os.path.dirname(dir) + "/" + name2 + " by typing\n" +
                    #                "  mv '" + dir + "' '" + os.path.dirname(dir) + "/" + name2 + "'")
                    dirname = os.path.dirname(dir) + "/" + name2
                    exstr = "package %s should have name %s\n" % (dir, name2)
                    exstr += "please rename it to %s by typing\n" % dirname
                    exstr += "  mv '%s' '%s'" % (dir, dirname)
                    raise Exception(exstr)
                pkg2 = self.m_packages.getPkg(name)
                if pkg2 and pkg2.release() == 0:
                    raise Exception("duplicate packages, please remove one of them:\n" +
                                    "  " + dir + "\n" +
                                    "  " + pkg2.srcdir())

                pkg = PackageInfo(self)
                pkg.m_name = name
                pkg.m_srcdir = dir
                pkg.m_release = False
                pkg.m_recompile = True
                self.m_packages.addPkg(pkg)
            if((not is_package or allowNesting) and os.path.isdir(dir) and
                (area != self.bin() or
                 (filename != "RootCore" and filename != "bin"          and
                  filename != "data"     and filename != "include"      and
                  filename != "lib"      and filename != "obj"          and 
                  filename != "python"   and filename != "user_scripts" ))):
                self.find_packages_find(dir, allowNesting)

    def find_packages_reorder(self, allowMiss, restrict):
        """effects: do the reordering of the packages based on
        dependencies for find_packages"""
        def addPkgDep(self, name, required, caller, cycle, catalog, allowMiss, wa):
            if self.getPkg(name):
                return
            for pkg in cycle:
                if name == pkg:
                    raise Exception("cyclical dependence involving packages: " +
                                    string.join(cycle))
            pkg = catalog.getPkg(name)
            if not pkg:
                if allowMiss or not required:
                    return
                message = ""
                if caller:
                    message = "package " + name + " not known, required by " + caller
                else:
                    message = "package " + name + " not known"
                shell_args = ["grep", "/" + name + "/tags", wa.dir() + "/all_packages"]
                known = shell_exec(shell_args, allowFail=True).split("\n")
                while len(known) > 0 and known[len(known) - 1] == "":
                    known.remove("")
                if len(known) > 0:
                    message += "\ntry checking it out with"
                    for pkg in known:
                        message += "\n  rc checkout_pkg " + pkg
                raise RCError(message)
            for dep in pkg.harddep():
                addPkgDep(self, dep, True, name, cycle + [name], catalog, allowMiss, wa)
            for dep in pkg.trydep():
                addPkgDep(self, dep, False, name, cycle + [name], catalog, allowMiss, wa)
            self.addPkg(pkg)

        ordered = PackageList(self)
        max_auto = 1
        while max_auto > 0:
            max_auto = 0
            for pkg in self.m_packages:
                if not ordered.getPkg(pkg.name()):
                    if pkg.auto() > max_auto:
                        max_auto = pkg.auto()
            if max_auto == 0:
                break
            for pkg in self.m_packages:
                if pkg.auto() == max_auto:
                    addPkgDep(ordered, pkg.name(), True, None, [],
                              self.m_packages, allowMiss, self)

        if len(restrict) > 0:
            for pkg in restrict:
                addPkgDep(ordered, pkg, True, None, [],
                          self.m_packages, allowMiss, self)
        else:
            for pkg in self.m_packages:
                addPkgDep(ordered, pkg.name(), True, None, [],
                          self.m_packages, allowMiss, self)
        self.m_packages = ordered

    def find_packages_dep_rebuild(self):
        """effects: set the determined dependencies and the rebuild
        flags for the find_packages operation"""
        myauto = []
        for pkg in self.m_packages:
            pkg.m_dep = []
            for dep in myauto :
                pkg.m_dep.append(dep)
            for dep in pkg.harddep() + pkg.trydep():
                pkg2 = self.m_packages.getPkg(dep)
                if pkg2:
                    for mydep in pkg2.dep():
                        if not mydep in pkg.m_dep:
                            pkg.m_dep.append(mydep)
                    if pkg2.recompile():
                        pkg.m_recompile = True
            pkg.m_dep.append(pkg.name())
            if pkg.auto() > 0 and pkg.name() != "RootCore" :
                for dep in pkg.m_dep :
                    if not dep in myauto :
                        myauto = myauto + [dep]
            if pkg.name() == "RootCore" or pkg.recompile() == True:
                pkg.m_outdir = self.obj()

    def find_packages_write(self):
        """effects: write out the result of the find_packages
        operation"""
        RootCore = self.m_packages.getPkg("RootCore")
        if RootCore.srcdir() != self.dir():
            print "WARNING: RootCore installation changed, please setup again in a new shell"
            self.m_dir = RootCore.srcdir()

        CXXFLAGS = ""
        LDFLAGS = ""
        if os.getenv("CXXFLAGS"):
            CXXFLAGS = os.getenv("CXXFLAGS")
        if os.getenv("LDFLAGS"):
            LDFLAGS = os.getenv("LDFLAGS")
        if os.getenv("EXTRA_CFLAGS"):
            CXXFLAGS += " " + os.getenv("EXTRA_CFLAGS")
        if os.getenv("CPPEXFLAGS"):
            CXXFLAGS += " " + os.getenv("CPPEXFLAGS")
            LDFLAGS += " " + os.getenv("CPPEXFLAGS")
        set_field(self.config(), "CXXFLAGS", CXXFLAGS)
        set_field(self.config(), "LDFLAGS", LDFLAGS)
        set_field(self.config(), "use_pedantic", str(self.m_pedantic))
        self.write_config()
        self.writePackages(self.m_packages)

    def find_packages(self, release, allowMiss, allowNesting):
        """effects: perform the find_packages command"""

        restrict = self.find_packages_release(release)

        print "looking for packages in " + self.m_area
        self.find_packages_find(self.m_area, allowNesting)

        self.m_packages.sortByName()
        print
        print "packages found:"
        for pkg in self.m_packages:
            print pkg.srcdir()

        self.find_packages_reorder(allowMiss, restrict)

        print
        print "sorted packages:"
        for pkg in self.m_packages:
            print pkg.srcdir()
        self.find_packages_dep_rebuild()
        # rationale: I am setting the recompile flag for RootCore,
        # because it's outdir always points to the current output
        # directory, even if it comes from the release.  I'm setting
        # this here, to avoid accidentally recompiling everything
        # depending on RootCore.
        self.m_packages.getPkg("RootCore").m_recompile = True
        print
        print "writing changes"
        self.find_packages_write()
        print "done"

    def compileCore(self, packages, log_files, cont) :
        """effects: do the core part of 'rc compile', i.e. compile all
        the packages"""

        failures = []
        for pkg in packages:
            log_file = None
            if log_files:
                path = pkg.nicos_name() + ".loglog"
                logdir = os.getenv("ROOTCORELOG")
                if logdir:
                    path = os.path.join(logdir, path)
                    pass
                print "writing " + pkg.name() + " compilation log to " + path
                log_file = open(path, "w")
            try:
                if not pkg.compile(ignoreErrors = cont, logFile = log_file):
                    raise RCError("failed to compile : " + pkg.name())
            except Exception as e:
                failures.append(pkg.name())
                if not cont:
                    raise
            finally:
                if log_file:
                    log_file.close()
                    pass
                pass
            pass

        if len(failures) > 0:
            raise RCError("compilation failures in: " + string.join(failures, " "))
        pass
    pass