mongo/bazel/install_rules/install_rules.py

125 lines
5.1 KiB
Python

import argparse
import json
import os
import shutil
import time
parser = argparse.ArgumentParser()
parser.add_argument("--depfile", action="append")
parser.add_argument("--install-dir")
parser.add_argument("--install-mode", choices=["copy", "symlink", "hardlink"], default="hardlink")
args = parser.parse_args()
if os.path.exists(args.install_dir):
os.chmod(args.install_dir, 0o755)
for root, dirs, files in os.walk(args.install_dir):
for name in files:
try:
os.chmod(os.path.join(root, name), 0o755)
os.unlink(os.path.join(root, name))
# Sometimes we find files that don't exist
# from os.walk - not sure why
except FileNotFoundError:
continue
for name in dirs:
try:
os.chmod(os.path.join(root, name), 0o755)
except FileNotFoundError:
continue
shutil.rmtree(args.install_dir)
os.makedirs(args.install_dir, exist_ok=True)
install_link = args.install_dir + "/../install"
os.makedirs(install_link, exist_ok=True)
def install(src, install_type):
install_dst = os.path.join(args.install_dir, install_type, os.path.basename(src))
link_dst = os.path.join(install_link, install_type, os.path.basename(src))
if src.endswith(".dwp"):
# Due to us creating our binaries using the _with_debug name
# the dwp files also contain it. Strip the _with_debug from the name
install_dst = install_dst.replace("_with_debug.dwp", ".dwp")
link_dst = link_dst.replace("_with_debug.dwp", ".dwp")
for dst in [install_dst, link_dst]:
os.makedirs(os.path.dirname(dst), exist_ok=True)
if args.install_mode == "hardlink":
if os.path.exists(dst) and not os.path.samefile(src, dst):
try:
if os.path.isdir(dst):
shutil.rmtree(dst)
else:
os.chmod(dst, 0o755)
os.unlink(dst)
except FileNotFoundError as exc:
if link_dst == dst:
# if multiple installs are requested at once and happen
# to install the same file, it is ambiguous
# when one should be linked into the general install dir
# so we pass on exceptions
pass
else:
raise exc
except OSError as exc:
if exc.strerror == "Directory not empty":
print("Encountered OSError: Directory not empty. Retrying...")
time.sleep(1)
shutil.rmtree(dst)
else:
raise exc
if not os.path.exists(dst):
try:
if os.path.isdir(src):
for root, _, files in os.walk(src):
for name in files:
dest_dir = os.path.dirname(os.path.join(root, name)).replace(
src, dst
)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
try:
os.link(os.path.join(root, name), os.path.join(dest_dir, name))
except OSError as exc:
if exc.strerror == "Invalid argument":
print("Encountered OSError: Invalid argument. Retrying...")
time.sleep(1)
os.link(
os.path.join(root, name), os.path.join(dest_dir, name)
)
else:
try:
os.link(src, dst)
# If you try hardlinking across drives link will fail
except OSError:
try:
shutil.copy(src, dst)
except shutil.SameFileError:
pass
except FileExistsError as exc:
if link_dst == dst:
# if multiple installs are requested at once and happen
# to install the same file, it is ambiguous
# when one should be linked into the general install dir
# so we pass on exceptions
pass
else:
raise exc
else:
raise Exception("Only hardlink mode is currently implemented.")
return install_dst
for depfile in args.depfile:
with open(depfile) as f:
content = json.load(f)
for binary in content["bins"]:
install(binary, "bin")
for lib in content["libs"]:
install(lib, "lib")
for file, folder in content["roots"].items():
install(file, folder)