예제 #1
0
def run():
    parser = ArgumentParser(description="""
        Query an attribute for the current project.
        Supported attributes:
        subprojects-dir : base directory where the dependencies will be extracted.
        """)
    parser.add_argument(
        "attribute",
        action="store",
        help=
        "The attribute to query. At the moment only subprojects-dir is supported"
    )
    parser.add_argument("--abspath",
                        action="store_true",
                        help="For path attributes print the absolute path.")

    args = parser.parse_args()
    source_dir = os.path.join(os.path.abspath("."), '')
    conf = load_conf(source_dir)
    if conf is None:
        print("This is not a quark project. Aborting.")
        sys.exit(1)
    attr = args.attribute
    if attr == "subprojects_dir":
        subprojects_dir = conf.get("subprojects_dir", 'lib')
        if args.abspath:
            print(os.path.join(source_dir, subprojects_dir))
        else:
            print(subprojects_dir)
    else:
        print("Unsupported attribute '%s'." % attr)
        sys.exit(1)
예제 #2
0
def generate_cmake_script(source_dir, url=None, options=None, print_tree=False,update=True):
    root, modules = Subproject.create_dependency_tree(source_dir, url, options, update=update)
    if print_tree:
        print(json.dumps(root.toJSON(), indent=4))
    conf = load_conf(source_dir)
    if update and conf is not None:
        subproject_dir = join(source_dir, conf.get("subprojects_dir", 'lib'))

        cmakelists_rows = []
        processed = set()

        def dump_options(module):
            for key, value in sorted(module.options.items()):
                if value is None:
                    cmakelists_rows.append('unset(%s CACHE)\n' % (key))
                    continue
                elif isinstance(value, bool):
                    kind = "BOOL"
                    value = 'ON' if value else 'OFF'
                else:
                    kind = "STRING"
                cmakelists_rows.append('set(%s %s CACHE INTERNAL "" FORCE)\n' % (key, value))

        def process_module(module):
            # notice: if a module is marked as excluded from cmake we also
            # exclude its dependencies; they are nonetheless included if they
            # are required by another module which is not excluded from cmake
            if module.name in processed or module.exclude_from_cmake:
                return
            processed.add(module.name)
            # first add the dependent modules
            # module.children is a set, whose iteration order changes from run to run
            # make this deterministic (we want to generate always the same CMakeLists.txt)
            for c in sorted(module.children, key = lambda x: x.name):
                process_module(c)
            # dump options and add to the generated CMakeLists.txt
            dump_options(module)
            if module is not root and exists(join(module.directory, "CMakeLists.txt")):
                cmakelists_rows.append('add_subdirectory(%s)\n' % (module.directory))

        process_module(root)

        # write only if different
        cmakelists_data = ''.join(cmakelists_rows)
        try:
            with open(join(subproject_dir, 'CMakeLists.txt'), 'r') as f:
                if cmakelists_data == f.read():
                    # nothing to do, avoid touching the file (which often yields a full rebuild)
                    return
        except IOError:
            pass
        # actually write the file
        with open(join(subproject_dir, 'CMakeLists.txt'), 'w') as f:
            f.write(cmakelists_data)
예제 #3
0
    def create_dependency_tree(source_dir,
                               url=None,
                               options=None,
                               update=False,
                               clean=False,
                               clobber=False):
        # make sure the separator is present
        source_dir_rp = os.path.join(os.path.abspath(source_dir), '')
        clobber_backup_path = os.path.join(source_dir_rp, 'clobbered.quark')
        if clobber and os.path.exists(clobber_backup_path):
            raise QuarkError(
                'clobbered.quark already exists; remove it to proceed')
        root_url = url
        try:
            root_url = url_from_directory(source_dir)
        except QuarkError:
            pass
        root = Subproject.create("root",
                                 root_url,
                                 source_dir, {}, {},
                                 toplevel=True)
        if url and update:
            root.checkout()
        conf = load_conf(source_dir)
        if conf is None:
            return root, {}
        subprojects_dir = conf.get("subprojects_dir", 'lib')
        subproject_dir = join(source_dir, subprojects_dir)
        stack = [root]
        modules = {}

        def get_option(key):
            try:
                return root.options[key]
            except KeyError as e:
                err = e
            for module in modules.values():
                try:
                    return module.options[key]
                except KeyError as e:
                    err = e
            raise err

        def add_module(parent, name, uri, options, conf, **kwargs):
            if uri is None:
                # options add only, lookup from existing modules
                uri = modules[name].urlstring
            target_dir = join(subproject_dir, name)
            target_dir_rp = os.path.join(os.path.abspath(target_dir), '')
            if not target_dir_rp.startswith(source_dir_rp):
                raise QuarkError(
                    """
Subproject `%s` (URI: %s)
is trying to escape from the main project directory (`%s`)
subproject abspath:   %s
main project abspath: %s""" %
                    (name, uri, source_dir, target_dir_rp, source_dir_rp))
            newmodule = Subproject.create(name, uri, target_dir, options, conf,
                                          **kwargs)
            mod = modules.setdefault(name, newmodule)
            if mod is newmodule:
                mod.parents.add(parent)
                if update:
                    if clobber and os.path.exists(mod.directory):
                        # if we have to clobber and a subproject directory
                        # already exists, move it out of the way

                        # ensure we have a backup root
                        os.makedirs(clobber_backup_path, exist_ok=True)
                        # find a name for the backup directory; we may have to
                        # deal with collisions, as the .. trick allows for
                        # duplicated basenames between subprojects
                        backup_path_base = os.path.join(
                            clobber_backup_path,
                            os.path.basename(mod.directory))
                        backup_path = backup_path_base
                        i = 0
                        while True:
                            if not os.path.exists(backup_path):
                                break
                            i += 1
                            backup_path = '%s.%d' % (backup_path_base, i)
                        print(
                            '%s already present; moving it to %s before new checkout'
                            % (name, backup_path))
                        shutil.move(mod.directory, backup_path)
                    mod.update(clean)
            else:
                if newmodule.exclude_from_cmake != mod.exclude_from_cmake:
                    children_conf = [
                        join(parent.directory, dependency_file)
                        for parent in mod.parents
                    ]
                    parent_conf = join(parent.directory, dependency_file)
                    raise ValueError(
                        "Conflicting value of 'exclude_from_cmake'"
                        " attribute for module '%s': %r required by %s and %r required by %s"
                        % (name, mod.exclude_from_cmake, children_conf,
                           newmodule.exclude_from_cmake, parent_conf))
                if not newmodule.same_checkout(mod) and uri is not None:
                    children = [
                        join(parent.directory, dependency_file)
                        for parent in mod.parents
                    ]
                    parent = join(parent.directory, dependency_file)
                    raise ValueError(
                        "Conflicting URLs for module '%s': '%s' required by %s and '%s' required by '%s'"
                        % (name, mod.urlstring, children, newmodule.urlstring,
                           parent))

                else:
                    for key, value in options.items():
                        mod.options.setdefault(key, value)
                        if mod.options[key] != value:
                            raise ValueError(
                                "Conflicting values option '%s' of module '%s'"
                                % (key, mod.name))
            stack.append(mod)
            parent.children.add(mod)

        freeze_conf = join(root.directory, freeze_file)
        if exists(freeze_conf):
            with open(freeze_conf, 'r') as f:
                freeze_dict = json.load(f)
        else:
            freeze_dict = {}
        if update:
            mkdir(subproject_dir)
        while len(stack):
            current_module = stack.pop()
            if current_module.external_project:
                generate_cmake_script(current_module.directory, update=update)
                continue
            conf = load_conf(current_module.directory)
            if conf:
                if current_module.toplevel:
                    current_module.options = conf.get('toplevel_options', {})
                    if options:
                        current_module.options.update(options)

                def do_add_module(name, depobject):
                    external_project = depobject.get('external_project', False)
                    add_module(current_module,
                               name,
                               freeze_dict.get(name,
                                               depobject.get('url', None)),
                               depobject.get('options', {}),
                               depobject,
                               exclude_from_cmake=depobject.get(
                                   'exclude_from_cmake', external_project),
                               external_project=external_project)

                for name, depobject in conf.get('depends', {}).items():
                    do_add_module(name, depobject)
                for key, optobjects in conf.get('optdepends', {}).items():
                    if isinstance(optobjects, dict):
                        optobjects = [optobjects]
                    for optobject in optobjects:
                        try:
                            value = get_option(key)
                        except KeyError:
                            continue
                        if value == optobject['value']:
                            for name, depobject in optobject['depends'].items(
                            ):
                                do_add_module(name, depobject)
        root.set_local_ignores(subprojects_dir, modules.values())
        return root, modules
예제 #4
0
    def create_dependency_tree(source_dir, url=None, options=None, update=False):
        root = Subproject.create("root", url, source_dir, {}, toplevel = True)
        if url and update:
            root.checkout()
        conf = load_conf(source_dir)
        if conf is None:
            return root, {}
        subproject_dir = join(source_dir, conf.get("subprojects_dir", 'lib'))
        stack = [root]
        modules = {}

        def get_option(key):
            try:
                return root.options[key]
            except KeyError as e:
                err = e
            for module in modules.values():
                try:
                    return module.options[key]
                except KeyError as e:
                    err = e
            raise err

        def add_module(parent, name, uri, options, **kwargs):
            if uri is None:
                # options add only, lookup from existing modules
                uri = modules[name].urlstring
            newmodule = Subproject.create(name, uri, join(subproject_dir, name), options, **kwargs)
            mod = modules.setdefault(name, newmodule)
            if mod is newmodule:
                mod.parents.add(parent)
                if update:
                    mod.update()
            else:
                if newmodule.exclude_from_cmake != mod.exclude_from_cmake:
                    children_conf = [join(parent.directory, dependency_file) for parent in mod.parents]
                    parent_conf = join(parent.directory, dependency_file)
                    raise ValueError("Conflicting value of 'exclude_from_cmake'"
                                     " attribute for module '%s': '%s' required by %s and %s required by %s" %
                                     (name, str(mod.exclude_from_cmake), children_conf, str(parent.exclude_from_cmake),
                                      parent_conf)
                                     )
                if not newmodule.same_checkout(mod) and uri is not None:
                    children = [join(parent.directory, dependency_file) for parent in mod.parents]
                    parent = join(parent.directory, dependency_file)
                    raise ValueError(
                        "Conflicting URLs for module '%s': '%s' required by %s and '%s' required by '%s'" %
                        (name,
                         mod.urlstring, children,
                         newmodule.urlstring, parent))

                else:
                    for key, value in options.items():
                        mod.options.setdefault(key, value)
                        if mod.options[key] != value:
                            raise ValueError(
                                "Conflicting values option '%s' of module '%s'" % (key, mod.name)
                            )
            stack.append(mod)
            parent.children.add(mod)

        freeze_conf = join(root.directory, freeze_file)
        if exists(freeze_conf):
            with open(freeze_conf, 'r') as f:
                freeze_dict = json.load(f)
        else:
            freeze_dict = {}
        if update:
            mkdir(subproject_dir)
        while len(stack):
            current_module = stack.pop()
            if current_module.external_project:
                generate_cmake_script(current_module.directory, update = update)
                continue
            conf = load_conf(current_module.directory)
            if conf:
                if current_module.toplevel:
                    current_module.options = conf.get('toplevel_options', {})
                    if options:
                        current_module.options.update(options)
                for name, depobject in conf.get('depends', {}).items():
                    external_project = depobject.get('external_project', False)
                    add_module(current_module, name,
                               freeze_dict.get(name, depobject.get('url', None)), depobject.get('options', {}),
                               exclude_from_cmake=depobject.get('exclude_from_cmake', external_project),
                               external_project=external_project
                               )
                for key, optobjects in conf.get('optdepends', {}).items():
                    if isinstance(optobjects, dict):
                        optobjects = [optobjects]
                    for optobject in optobjects:
                        try:
                            value = get_option(key)
                        except KeyError:
                            continue
                        if value == optobject['value']:
                            for name, depobject in optobject['depends'].items():
                                add_module(current_module, name,
                                           freeze_dict.get(name, depobject.get('url', None)),
                                           depobject.get('options', {}))
        return root, modules
예제 #5
0
    def create_dependency_tree(source_dir, url=None, options=None, update=False, clean=False):
        # make sure the separator is present
        source_dir_rp = os.path.join(os.path.abspath(source_dir), '')
        root_url = url
        try:
            root_url = url_from_directory(source_dir)
        except QuarkError:
            pass
        root = Subproject.create("root", root_url, source_dir, {}, {}, toplevel = True)
        if url and update:
            root.checkout()
        conf = load_conf(source_dir)
        if conf is None:
            return root, {}
        subprojects_dir = conf.get("subprojects_dir", 'lib')
        subproject_dir = join(source_dir, subprojects_dir)
        stack = [root]
        modules = {}

        def get_option(key):
            try:
                return root.options[key]
            except KeyError as e:
                err = e
            for module in modules.values():
                try:
                    return module.options[key]
                except KeyError as e:
                    err = e
            raise err

        def add_module(parent, name, uri, options, conf, **kwargs):
            if uri is None:
                # options add only, lookup from existing modules
                uri = modules[name].urlstring
            target_dir = join(subproject_dir, name)
            target_dir_rp = os.path.join(os.path.abspath(target_dir), '')
            if not target_dir_rp.startswith(source_dir_rp):
                raise QuarkError("""
Subproject `%s` (URI: %s)
is trying to escape from the main project directory (`%s`)
subproject abspath:   %s
main project abspath: %s""" % (name, uri, source_dir, target_dir_rp, source_dir_rp))
            newmodule = Subproject.create(name, uri, target_dir, options, conf, **kwargs)
            mod = modules.setdefault(name, newmodule)
            if mod is newmodule:
                mod.parents.add(parent)
                if update:
                    mod.update(clean)
            else:
                if newmodule.exclude_from_cmake != mod.exclude_from_cmake:
                    children_conf = [join(parent.directory, dependency_file) for parent in mod.parents]
                    parent_conf = join(parent.directory, dependency_file)
                    raise ValueError("Conflicting value of 'exclude_from_cmake'"
                                     " attribute for module '%s': %r required by %s and %r required by %s" %
                                     (name, mod.exclude_from_cmake, children_conf, newmodule.exclude_from_cmake,
                                      parent_conf)
                                     )
                if not newmodule.same_checkout(mod) and uri is not None:
                    children = [join(parent.directory, dependency_file) for parent in mod.parents]
                    parent = join(parent.directory, dependency_file)
                    raise ValueError(
                        "Conflicting URLs for module '%s': '%s' required by %s and '%s' required by '%s'" %
                        (name,
                         mod.urlstring, children,
                         newmodule.urlstring, parent))

                else:
                    for key, value in options.items():
                        mod.options.setdefault(key, value)
                        if mod.options[key] != value:
                            raise ValueError(
                                "Conflicting values option '%s' of module '%s'" % (key, mod.name)
                            )
            stack.append(mod)
            parent.children.add(mod)

        freeze_conf = join(root.directory, freeze_file)
        if exists(freeze_conf):
            with open(freeze_conf, 'r') as f:
                freeze_dict = json.load(f)
        else:
            freeze_dict = {}
        if update:
            mkdir(subproject_dir)
        while len(stack):
            current_module = stack.pop()
            if current_module.external_project:
                generate_cmake_script(current_module.directory, update = update)
                continue
            conf = load_conf(current_module.directory)
            if conf:
                if current_module.toplevel:
                    current_module.options = conf.get('toplevel_options', {})
                    if options:
                        current_module.options.update(options)
                for name, depobject in conf.get('depends', {}).items():
                    external_project = depobject.get('external_project', False)
                    add_module(current_module, name,
                               freeze_dict.get(name, depobject.get('url', None)), depobject.get('options', {}),
                               depobject,
                               exclude_from_cmake=depobject.get('exclude_from_cmake', external_project),
                               external_project=external_project,
                               )
                for key, optobjects in conf.get('optdepends', {}).items():
                    if isinstance(optobjects, dict):
                        optobjects = [optobjects]
                    for optobject in optobjects:
                        try:
                            value = get_option(key)
                        except KeyError:
                            continue
                        if value == optobject['value']:
                            for name, depobject in optobject['depends'].items():
                                add_module(current_module, name,
                                           freeze_dict.get(name, depobject.get('url', None)),
                                           depobject.get('options', {}),
                                           depobject)
        root.set_local_ignores(subprojects_dir, modules.values())
        return root, modules