# Copyright 2011-2015 The Rust Project Developers. See the COPYRIGHT # file at the top-level directory of this distribution and at # http://rust-lang.org/COPYRIGHT. # # Licensed under the Apache License, Version 2.0 or the MIT license # , at your # option. This file may not be copied, modified, or distributed # except according to those terms. import re import os import sys import glob import tarfile import shutil import subprocess import distutils.spawn try: import hashlib sha_func = hashlib.sha1 except ImportError: import sha sha_func = sha.new def scrub(b): if sys.version_info >= (3,) and type(b) == bytes: return b.decode('ascii') else: return b src_dir = scrub(os.getenv("CFG_SRC_DIR")) if not src_dir: raise Exception("missing env var CFG_SRC_DIR") snapshotfile = os.path.join(src_dir, "src", "snapshots.txt") download_url_base = "https://static.rust-lang.org/stage0-snapshots" download_dir_base = "dl" download_unpack_base = os.path.join(download_dir_base, "unpack") snapshot_files = { "bitrig": ["bin/rustc"], "dragonfly": ["bin/rustc"], "freebsd": ["bin/rustc"], "linux": ["bin/rustc"], "macos": ["bin/rustc"], "netbsd": ["bin/rustc"], "openbsd": ["bin/rustc"], "winnt": ["bin/rustc.exe"], } winnt_runtime_deps_32 = ["libgcc_s_dw2-1.dll", "libstdc++-6.dll"] winnt_runtime_deps_64 = ["libgcc_s_seh-1.dll", "libstdc++-6.dll"] def parse_line(n, line): global snapshotfile if re.match(r"\s*$", line): return None if re.match(r"^T\s*$", line): return None match = re.match(r"\s+([\w_-]+) ([a-fA-F\d]{40})\s*$", line) if match: return {"type": "file", "platform": match.group(1), "hash": match.group(2).lower()} match = re.match(r"([ST]) (\d{4}-\d{2}-\d{2}) ([a-fA-F\d]+)\s*$", line) if not match: raise Exception("%s:%d:E syntax error: " % (snapshotfile, n)) return {"type": "snapshot", "date": match.group(2), "rev": match.group(3)} def partial_snapshot_name(date, rev, platform): return ("rust-stage0-%s-%s-%s.tar.bz2" % (date, rev, platform)) def full_snapshot_name(date, rev, platform, hsh): return ("rust-stage0-%s-%s-%s-%s.tar.bz2" % (date, rev, platform, hsh)) def get_kernel(triple): t = triple.split('-') if len(t) == 2: os_name = t[1] else: os_name = t[2] if os_name == "windows": return "winnt" if os_name == "darwin": return "macos" if os_name == "freebsd": return "freebsd" if os_name == "dragonfly": return "dragonfly" if os_name == "bitrig": return "bitrig" if os_name == "netbsd": return "netbsd" if os_name == "openbsd": return "openbsd" return "linux" def get_cpu(triple): arch = triple.split('-')[0] if arch == "i686": return "i386" return arch def get_platform(triple): return "%s-%s" % (get_kernel(triple), get_cpu(triple)) def cmd_out(cmdline): p = subprocess.Popen(cmdline, stdout=subprocess.PIPE) return scrub(p.communicate()[0].strip()) def local_rev_info(field): return cmd_out(["git", "--git-dir=" + os.path.join(src_dir, ".git"), "log", "-n", "1", "--format=%%%s" % field, "HEAD"]) def local_rev_full_sha(): return local_rev_info("H").split()[0] def local_rev_short_sha(): return local_rev_info("h").split()[0] def local_rev_committer_date(): return local_rev_info("ci") def get_url_to_file(u, f): # no security issue, just to stop partial download leaving a stale file tmpf = f + '.tmp' returncode = -1 if distutils.spawn.find_executable("curl"): returncode = subprocess.call(["curl", "-o", tmpf, u]) elif distutils.spawn.find_executable("wget"): returncode = subprocess.call(["wget", "-O", tmpf, u]) if returncode != 0: try: os.unlink(tmpf) except OSError: pass raise Exception("failed to fetch url") os.rename(tmpf, f) def snap_filename_hash_part(snap): match = re.match(r".*([a-fA-F\d]{40}).tar.bz2$", snap) if not match: raise Exception("unable to find hash in filename: " + snap) return match.group(1) def hash_file(x): h = sha_func() h.update(open(x, "rb").read()) return scrub(h.hexdigest()) def get_winnt_runtime_deps(platform): """Returns a list of paths of Rust's system runtime dependencies""" if platform == "winnt-x86_64": deps = winnt_runtime_deps_64 else: deps = winnt_runtime_deps_32 runtime_deps = [] path_dirs = os.environ["PATH"].split(os.pathsep) for name in deps: for dir in path_dirs: filepath = os.path.join(dir, name) if os.path.isfile(filepath): runtime_deps.append(filepath) break else: raise Exception("Could not find runtime dependency: %s" % name) return runtime_deps def make_snapshot(stage, triple): kernel = get_kernel(triple) platform = get_platform(triple) rev = local_rev_short_sha() date = local_rev_committer_date().split()[0] file0 = partial_snapshot_name(date, rev, platform) def in_tar_name(fn): cs = re.split(r"[\\/]", fn) if len(cs) >= 2: return os.sep.join(cs[-2:]) tar = tarfile.open(file0, "w:bz2") for name in snapshot_files[kernel]: dir = stage if stage == "stage1" and re.match(r"^lib/(lib)?std.*", name): dir = "stage0" fn_glob = os.path.join(triple, dir, name) matches = glob.glob(fn_glob) if not matches: raise Exception("Not found file with name like " + fn_glob) if len(matches) == 1: tar.add(matches[0], "rust-stage0/" + in_tar_name(matches[0])) else: raise Exception("Found stale files: \n %s\n" "Please make a clean build." % "\n ".join(matches)) if kernel == "winnt": for path in get_winnt_runtime_deps(platform): tar.add(path, "rust-stage0/bin/" + os.path.basename(path)) tar.add(os.path.join(os.path.dirname(__file__), "third-party"), "rust-stage0/bin/third-party") tar.close() h = hash_file(file0) file1 = full_snapshot_name(date, rev, platform, h) shutil.move(file0, file1) return file1 def curr_snapshot_rev(): i = 0 found_snap = False date = None rev = None f = open(snapshotfile) for line in f.readlines(): i += 1 parsed = parse_line(i, line) if not parsed: continue if parsed["type"] == "snapshot": date = parsed["date"] rev = parsed["rev"] found_snap = True break if not found_snap: raise Exception("no snapshot entries in file") return (date, rev) def determine_curr_snapshot(triple): i = 0 platform = get_platform(triple) found_file = False found_snap = False hsh = None date = None rev = None f = open(snapshotfile) for line in f.readlines(): i += 1 parsed = parse_line(i, line) if not parsed: continue if found_snap and parsed["type"] == "file": if parsed["platform"] == platform: hsh = parsed["hash"] found_file = True break elif parsed["type"] == "snapshot": date = parsed["date"] rev = parsed["rev"] found_snap = True if not found_snap: raise Exception("no snapshot entries in file") if not found_file: raise Exception("no snapshot file found for platform %s, rev %s" % (platform, rev)) return full_snapshot_name(date, rev, platform, hsh)