summaryrefslogtreecommitdiffstats
path: root/comm/build/moz.configure/gecko_source.configure
blob: 319150c81a05f97d82bf22e925fe64d1bd4495e9 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
#  This Source Code Form is subject to the terms of the Mozilla Public
#  License, v. 2.0. If a copy of the MPL was not distributed with this
#  file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Attempt to ascertain the Gecko source repository information.
# We need to have accurate source repository information for MPL compliance.


def get_fail_msg(source_name, repo_name, rev_name):
    return """Unable to determine {} source repository.
Try setting {} and {}
environment variables or build from a Mercurial checkout.""".format(
        source_name, repo_name, rev_name
    )


# Wrap check_cmd_output so that it does not fatally end configure on command
# failure.
def hg_cmd_output(*args, **kwargs):
    def hg_error():
        return None

    kwargs["onerror"] = hg_error

    return check_cmd_output(*args, **kwargs)


@template
def read_sourcestamp(repository):
    """
    Last resort, look for the revision data in the sourcestamp file.
    This file only exists in release tar files created in CI.
    repository must be one of "GECKO" or "COMM".
    """
    log.info("Determining %s source information from sourcestamp.txt..." % repository)

    line2read = {"COMM": 1, "GECKO": 2}[repository]

    @depends(build_environment)
    @imports(_from="os.path", _import="exists")
    @imports(_from="os.path", _import="join")
    @imports(_from="__builtin__", _import="open")
    def get_sourcestamp(build_env):
        sourcestamp_file = join(build_env.topsrcdir, "sourcestamp.txt")
        if exists(sourcestamp_file):
            try:
                lines = open(sourcestamp_file).readlines()
            except:
                pass

            if len(lines) != 3:
                log.warn("sourcestamp.txt is corrupt!")
                return

            if lines and lines[line2read].startswith("http"):
                repo_line = lines[line2read]
                repo_url = repo_line.split("/rev/")
                return namespace(repo_url=repo_url[0], repo_rev=repo_url[1])

    return get_sourcestamp


@template
def read_gecko_rev_yml():
    def get_value(x):
        return x.split()[1]

    @depends(commtopsrcdir)
    @imports(_from="os.path", _import="exists")
    @imports(_from="os.path", _import="join")
    @imports(_from="__builtin__", _import="open")
    def wrapped(commtopsrcdir):
        log.info("Determining GECKO source information from .gecko_rev.yml")
        rev_file = join(commtopsrcdir, ".gecko_rev.yml")
        if not exists(rev_file):
            return

        repo = rev = ref = None

        for line in open(rev_file).readlines():
            if line.startswith("GECKO_HEAD_REPOSITORY:"):
                repo = get_value(line)
            elif line.startswith("GECKO_HEAD_REV:"):
                rev = get_value(line)
            elif line.startswith("GECKO_HEAD_REF:"):
                ref = get_value(line)
            else:
                pass

        return namespace(repo=repo, rev=rev, ref=ref)

    return wrapped


@depends(application)
@imports(_from="os", _import="environ")
def comm_repo_from_environ(app):
    """
    Read the Thunderbird source repository information from the environment.

    Taskcluster builds set COMM_HEAD_REPOSITORY and COMM_HEAD_REV pointing
    to the comm-* repository.
    """
    log.info("Determining COMM source information from environment...")
    comm_repo = environ.get("COMM_HEAD_REPOSITORY", None)
    comm_rev = environ.get("COMM_HEAD_REV", None)

    if all([comm_repo, comm_rev]):
        log.info("{}/rev/{}".format(comm_repo, comm_rev))
        return namespace(comm_repo=comm_repo, comm_rev=comm_rev)


# Read sourcestamp.txt and return the Thunderbird source URL (with changeset).
# Silent fail if the file cannot be read.
comm_sourcestamp = read_sourcestamp("COMM")


@depends(comm_repo_from_environ, commtopsrcdir, hg, comm_sourcestamp)
@imports(_from="os", _import="environ")
def comm_repo_heuristics(comm_environ, commtopsrcdir, hg, sourcestamp):
    """
    Determine the Thunderbird Mercurial repository and revision from Mercurial
    or sourcestamp.txt when COMM_HEAD_REPOSITORY and COMM_HEAD_REV are unset
    (local developer builds).
    """
    if not comm_environ:
        comm_repo = comm_rev = None
        if hg:
            log.info("Determining COMM source information from Mercurial...")
            comm_rev = hg_cmd_output(hg, "-R", commtopsrcdir, "parent", "--template={node}")
            comm_repo = hg_cmd_output(hg, "-R", commtopsrcdir, "path", "default")
            if comm_repo:
                comm_repo = comm_repo.strip()
                if comm_repo.startswith("ssh://"):
                    comm_repo = "https://" + comm_repo[6:]
                comm_repo = comm_repo.rstrip("/")
        # TODO: git-cinnabar support?

        if not comm_repo or not comm_rev:
            try:
                comm_repo, comm_rev = sourcestamp.repo_url, sourcestamp.repo_rev
            except:
                pass

        if comm_repo and comm_rev:
            return namespace(comm_repo=comm_repo, comm_rev=comm_rev)


@depends(comm_repo_from_environ, comm_repo_heuristics, "MOZ_AUTOMATION")
@imports(_from="os", _import="environ")
def comm_source_repo(from_environ, from_config, automation):
    rv = None
    if from_environ:
        rv = from_environ
    elif from_config:
        rv = from_config
    elif automation:
        die(get_fail_msg("COMM", "COMM_HEAD_REPOSITORY", "COMM_HEAD_REV"))
    else:
        log.info(get_fail_msg("COMM", "COMM_HEAD_REPOSITORY", "COMM_HEAD_REV"))
        rv = namespace(comm_repo="unknown", comm_rev="unknown")

    log.info("COMM_SOURCE_REPOSITORY: {}".format(rv.comm_repo))
    log.info("COMM_SOURCE_CHANGESET: {}".format(rv.comm_rev))

    # Used by old-configure to set in buildconfig. This is configure's environment
    # not the same as used by the build itself.
    environ["MOZ_SOURCE_REPO"] = rv.comm_repo
    environ["MOZ_SOURCE_CHANGESET"] = rv.comm_rev

    return rv


@depends(application)
@imports(_from="os", _import="environ")
def gecko_repo_from_environ(app):
    """
    Same as above, but this time checking for the mozilla- repository.
    """
    log.info("Determining GECKO source information from environment...")
    gecko_repo = environ.get("GECKO_HEAD_REPOSITORY", None)
    gecko_rev = environ.get("GECKO_HEAD_REV", None)
    if all([gecko_repo, gecko_rev]):
        log.info("{}/rev/{}".format(gecko_repo, gecko_rev))
        return namespace(gecko_repo=gecko_repo, gecko_rev=gecko_rev)


# Read sourcestamp.txt, this time returning the mozilla- data
gecko_sourcestamp = read_sourcestamp("GECKO")
# Look in comm/.gecko_rev.yml fpr repository information
gecko_yml = read_gecko_rev_yml()


@depends(gecko_repo_from_environ, build_environment, hg, gecko_sourcestamp, gecko_yml)
@imports(_from="os.path", _import="join")
@imports(_from="os.path", _import="exists")
def gecko_repo_heuristics(gecko_environ, build_env, hg, sourcestamp, gecko_yml):
    """
    Look for the source repository and changeset for the mozilla- repository
    when the Taskcluster environment variables are not set, checking
    .gecko_rev.yml before falling back to Mercurial and sourcestamp.txt.
    """
    if not gecko_environ:
        gecko_repo = gecko_rev = gecko_ref = None

        try:
            gecko_repo = gecko_yml.repo
            gecko_rev = gecko_yml.rev
            gecko_ref = gecko_yml.ref
        except:
            pass

        if gecko_repo:
            if not gecko_rev and gecko_ref:
                # gecko_repo is known, but we have a branch ref like
                # "default" when a revision hash is needed. Try to query
                # Mercurial first.
                if hg:
                    log.info("Determining GECKO source information from Mercurial...")
                    gecko_rev = hg_cmd_output(
                        hg, "-R", build_env.topsrcdir, "parent", "--template={node}"
                    )
                # TODO: git-cinnabar support?

        if not gecko_repo or not gecko_rev:
            # See if we have a sourcestamp file. Last ditch effort!
            try:
                gecko_repo, gecko_rev = sourcestamp.repo_url, sourcestamp.repo_rev
            except:
                pass

        # Check one last time to see if both gecko_repo and gecko_rev
        # are set
        if gecko_repo and gecko_rev:
            return namespace(gecko_repo=gecko_repo, gecko_rev=gecko_rev)


@depends(gecko_repo_from_environ, gecko_repo_heuristics, "MOZ_AUTOMATION")
def gecko_source_repo(from_environ, from_heuristics, automation):
    rv = None
    if from_environ:
        rv = from_environ
    elif from_heuristics:
        rv = from_heuristics
    elif automation:
        die(get_fail_msg("GECKO", "GECKO_HEAD_REPOSITORY", "GECKO_HEAD_REV"))
    else:
        log.info(get_fail_msg("GECKO", "GECKO_HEAD_REPOSITORY", "GECKO_HEAD_REV"))
        rv = namespace(gecko_repo="unknown", gecko_rev="unknown")

    log.info("GECKO_SOURCE_REPOSITORY: {}".format(rv.gecko_repo))
    log.info("GECKO_SOURCE_CHANGESET: {}".format(rv.gecko_rev))
    return rv


set_config("MOZ_COMM_SOURCE_REPO", comm_source_repo.comm_repo)
set_config("MOZ_COMM_SOURCE_CHANGESET", comm_source_repo.comm_rev)
set_config("MOZ_GECKO_SOURCE_REPO", gecko_source_repo.gecko_repo)
set_config("MOZ_GECKO_SOURCE_CHANGESET", gecko_source_repo.gecko_rev)