# -*- 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)