summaryrefslogtreecommitdiffstats
path: root/scripts/osmo-depcheck/dependencies.py
blob: 9b5187d7040bf0ad29d0b1930957056e6b42dec6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2018 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>

import collections
import os
import subprocess
import sys

# Same folder
import parse


def git_clone(workdir, prefix, cache_git_fetch, repository, version):
    """ Clone a missing git repository and checkout a specific version tag.

        :param workdir: path to where all data (git, build, install) is stored
        :param prefix: git url prefix (e.g. "git://git.osmocom.org/")
        :param cache_git_fetch: list of repositories that have already been
                                fetched in this run of osmo-depcheck
        :param repository: Osmocom git repository name (e.g. "libosmo-abis")
        :param version: "master" or a version tag like "0.11.0" """
    repodir = workdir + "/git/" + repository
    if repository not in cache_git_fetch:
        if os.path.exists(repodir):
            # Fetch tags for existing source
            print("Fetching tags...")
            subprocess.run(["git", "-C", repodir, "fetch", "--tags", "-q"],
                           check=True)
        else:
            # Clone the source
            url = prefix + repository
            print("Cloning git repo: " + url)
            try:
                subprocess.run(["git", "-C", workdir + "/git", "clone", "-q",
                                url], check=True)
            except subprocess.CalledProcessError:
                print("NOTE: if '" + repository + "' is part of a git"
                      " repository with a different name, please add it to the"
                      " mapping in 'config.py' and try again.")
                sys.exit(1)

        # Only fetch the same repository once per session
        cache_git_fetch.append(repository)

    # Checkout the version tag
    try:
        subprocess.run(["git", "-C", repodir, "checkout", version, "-q"],
                       check=True)
    except subprocess.CalledProcessError:
        print("ERROR: git checkout failed! Invalid version specified?")
        sys.exit(1)


def generate(workdir, prefix, cache_git_fetch, initial, rev):
    """ Generate the dependency graph of an Osmocom program by cloning the git
        repository, parsing the "configure.ac" file, and recursing.

        :param workdir: path to where all data (git, build, install) is stored
        :param prefix: git url prefix (e.g. "git://git.osmocom.org/")
        :param cache_git_fetch: list of repositories that have already been
                                fetched in this run of osmo-depcheck
        :param initial: the first program to look at (e.g. "osmo-bts")
        :param rev: the git revision to check out ("master", "0.1.0", ...)
        :returns: a dictionary like the following:
                  {"osmo-bts": {"version": "master",
                                "depends": {"libosmocore": "0.11.0",
                                            "libosmo-abis": "0.5.0"}},
                   "libosmocore": {"version": "0.11.0",
                                   "depends": {}},
                   "libosmo-abis": {"version": "0.5.0",
                                    "depends": {"libosmocore": "0.11.0"}} """
    # Iterate over stack
    stack = collections.OrderedDict({initial: rev})
    ret = collections.OrderedDict()
    while len(stack):
        # Pop program from stack
        program, version = next(iter(stack.items()))
        del stack[program]

        # Skip when already parsed
        if program in ret:
            continue

        # Add the programs dependencies to the stack
        print("Looking at " + program + ":" + version)
        git_clone(workdir, prefix, cache_git_fetch, program, version)
        depends = parse.configure_ac(workdir, program)
        stack.update(depends)

        # Add the program to the ret
        ret[program] = {"version": version, "depends": depends}

    return ret


def print_dict(depends):
    """ Print the whole dependency graph.
        :param depends: return value from generate() above """
    print("Dependency graph:")

    for program, data in depends.items():
        version = data["version"]
        depends = data["depends"]
        print(" * " + program + ":" + version + " depends: " + str(depends))


def git_latest_tag(workdir, repository):
    """ Get the last release string by asking git for the latest tag.

        :param workdir: path to where all data (git, build, install) is stored
        :param repository: Osmocom git repository name (e.g. "libosmo-abis")
        :returns: the latest git tag (e.g. "1.0.2") """
    dir = workdir + "/git/" + repository
    complete = subprocess.run(["git", "-C", dir, "describe", "--abbrev=0",
                               "master"], check=True, stdout=subprocess.PIPE)
    return complete.stdout.decode().rstrip()


def print_old(workdir, depends):
    """ Print dependencies tied to an old release tag

        :param workdir: path to where all data (git, build, install) is stored
        :param depends: return value from generate() above """
    print("Dependencies on old releases:")

    for program, data in depends.items():
        for depend, version in data["depends"].items():
            latest = git_latest_tag(workdir, depend)
            if latest == version:
                continue
            print(" * " + program + ":" + data["version"] + " -> " +
                  depend + ":" + version + " (latest: " + latest + ")")