1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 15:24:46 -05:00
denoland-deno/tools/util.py

176 lines
5.3 KiB
Python
Raw Normal View History

# Copyright 2018 the Deno authors. All rights reserved. MIT license.
import os
2018-08-19 03:00:34 -04:00
import re
import shutil
import stat
2018-07-23 20:50:11 -04:00
import sys
import subprocess
2018-07-21 19:08:24 -04:00
executable_suffix = ".exe" if os.name == "nt" else ""
root_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
2018-07-21 19:08:24 -04:00
def make_env(merge_env={}, env=None):
if env is None:
env = os.environ
env = env.copy()
for key in merge_env.keys():
env[key] = merge_env[key]
return env
def run(args, quiet=False, cwd=None, env=None, merge_env={}):
args[0] = os.path.normpath(args[0])
2018-07-16 22:40:42 -04:00
if not quiet:
print " ".join(args)
env = make_env(env=env, merge_env=merge_env)
2018-07-21 19:08:24 -04:00
shell = os.name == "nt" # Run through shell to make .bat/.cmd files work.
2018-07-23 20:50:11 -04:00
rc = subprocess.call(args, cwd=cwd, env=env, shell=shell)
if rc != 0:
sys.exit(rc)
def run_output(args, quiet=False, cwd=None, env=None, merge_env={}):
args[0] = os.path.normpath(args[0])
if not quiet:
print " ".join(args)
env = make_env(env=env, merge_env=merge_env)
shell = os.name == "nt" # Run through shell to make .bat/.cmd files work.
return subprocess.check_output(args, cwd=cwd, env=env, shell=shell)
def remove_and_symlink(target, name, target_is_dir=False):
try:
# On Windows, directory symlink can only be removed with rmdir().
if os.name == "nt" and os.path.isdir(name):
os.rmdir(name)
else:
os.unlink(name)
except:
pass
symlink(target, name, target_is_dir)
def symlink(target, name, target_is_dir=False):
if os.name == "nt":
from ctypes import WinDLL, WinError, GetLastError
from ctypes.wintypes import BOOLEAN, DWORD, LPCWSTR
kernel32 = WinDLL('kernel32', use_last_error=False)
CreateSymbolicLinkW = kernel32.CreateSymbolicLinkW
CreateSymbolicLinkW.restype = BOOLEAN
CreateSymbolicLinkW.argtypes = (LPCWSTR, LPCWSTR, DWORD)
# File-type symlinks can only use backslashes as separators.
target = os.path.normpath(target)
# If the symlink points at a directory, it needs to have the appropriate
# flag set, otherwise the link will be created but it won't work.
if target_is_dir:
type_flag = 0x01 # SYMBOLIC_LINK_FLAG_DIRECTORY
else:
type_flag = 0
# Before Windows 10, creating symlinks requires admin privileges.
# As of Win 10, there is a flag that allows anyone to create them.
# Initially, try to use this flag.
unpriv_flag = 0x02 # SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
r = CreateSymbolicLinkW(name, target, type_flag | unpriv_flag)
# If it failed with ERROR_INVALID_PARAMETER, try again without the
# 'allow unprivileged create' flag.
if not r and GetLastError() == 87: # ERROR_INVALID_PARAMETER
r = CreateSymbolicLinkW(name, target, type_flag)
# Throw if unsuccessful even after the second attempt.
if not r:
raise WinError()
else:
os.symlink(target, name)
def touch(fname):
if os.path.exists(fname):
os.utime(fname, None)
else:
open(fname, 'a').close()
# Recursive search for files of certain extensions.
# (Recursive glob doesn't exist in python 2.7.)
def find_exts(directory, *extensions):
matches = []
for root, dirnames, filenames in os.walk(directory):
for filename in filenames:
for ext in extensions:
if filename.endswith(ext):
matches.append(os.path.join(root, filename))
break
return matches
# The Python equivalent of `rm -rf`.
def rmtree(directory):
# On Windows, shutil.rmtree() won't delete files that have a readonly bit.
# Git creates some files that do. The 'onerror' callback deals with those.
def rm_readonly(func, path, _):
os.chmod(path, stat.S_IWRITE)
func(path)
shutil.rmtree(directory, onerror=rm_readonly)
def build_mode(default="debug"):
if "DENO_BUILD_MODE" in os.environ:
return os.environ["DENO_BUILD_MODE"]
else:
return default
# E.G. "out/debug"
def build_path():
if "DENO_BUILD_PATH" in os.environ:
return os.environ["DENO_BUILD_PATH"]
else:
return os.path.join(root_path, "out", build_mode())
# Returns True if the expected matches the actual output, allowing variation
# from actual where expected has the wildcard (e.g. matches /.*/)
def pattern_match(pattern, string, wildcard="[WILDCARD]"):
if len(pattern) == 0:
return string == 0
if pattern == wildcard:
return True
parts = str.split(pattern, wildcard)
if len(parts) == 1:
return pattern == string
if string.startswith(parts[0]):
string = string[len(parts[0]):]
else:
return False
for i in range(1, len(parts)):
if i == (len(parts) - 1):
if parts[i] == "" or parts[i] == "\n":
return True
found = string.find(parts[i])
if found < 0:
return False
string = string[(found + len(parts[i])):]
return len(string) == 0
2018-08-19 03:00:34 -04:00
def parse_exit_code(s):
codes = [int(d or 1) for d in re.findall(r'error(\d*)', s)]
if len(codes) > 1:
assert False, "doesn't support multiple error codes."
elif len(codes) == 1:
return codes[0]
else:
return 0