teleport/build/builder/core/utils.py

435 lines
12 KiB
Python

# -*- coding: utf8 -*-
import os
import platform
import shutil
import subprocess
import sys
import time
from . import colorconsole as cc
from .env import env
from .configs import cfg
try:
CONFIG_FILE = os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')), 'config.ini')
if not cfg.init(CONFIG_FILE):
sys.exit(1)
except:
cc.e('can not load configuration.\n\nplease copy `config.ini.in` into `config.ini` and modify it to fit your condition and try again.')
sys.exit(1)
if cfg.is_py2:
import imp
elif cfg.is_py3:
import importlib
import importlib.machinery
if sys.platform == 'win32':
import winreg
THIS_PATH = os.path.abspath(os.path.dirname(__file__))
ROOT_PATH = os.path.abspath(os.path.join(THIS_PATH, '..'))
def download_file(desc, url, target_path, file_name):
cc.n('downloading {} ...'.format(desc))
local_file_name = os.path.join(target_path, file_name)
if os.path.exists(local_file_name):
cc.w('already exists, skip.')
return True
if env.is_win:
cmd = '"{}" --no-check-certificate {} -O "{}"'.format(env.wget, url, local_file_name)
sys_exec(cmd, True)
elif env.is_linux:
os.system('wget --no-check-certificate {} -O "{}"'.format(url, local_file_name))
else:
return False
if not os.path.exists(local_file_name):
cc.e('downloading {} from {} failed.'.format(desc, url))
return False
return True
def extension_suffixes():
# imp.get_suffixes()
# 返回3元组列表(suffix, mode, type), 获得特殊模块的描述
# .suffix为文件后缀名;
# mode为打开文件模式;
# type为文件类型, 1代表PY_SOURCE, 2代表PY_COMPILED, 3代表C_EXTENSION
EXTENSION_SUFFIXES = list()
if cfg.is_py2:
suf = imp.get_suffixes()
for s in suf:
if s[2] == 3:
EXTENSION_SUFFIXES.append(s[0])
else:
EXTENSION_SUFFIXES = importlib.machinery.EXTENSION_SUFFIXES
if cfg.dist == 'windows':
if '.dll' not in EXTENSION_SUFFIXES:
EXTENSION_SUFFIXES.append('.dll')
elif cfg.dist == 'linux':
if '.so' not in EXTENSION_SUFFIXES:
EXTENSION_SUFFIXES.append('.so')
elif cfg.dist == 'macos':
raise RuntimeError('not support MacOS now.')
return EXTENSION_SUFFIXES
def remove(*args):
path = os.path.join(*args)
cc.v('remove [%s] ...' % path, end='')
if not os.path.exists(path):
cc.v('not exists, skip.')
return
for i in range(5):
cc.v('.', end='')
try:
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
time.sleep(0.5)
else:
os.unlink(path)
except:
pass
if os.path.exists(path):
time.sleep(1)
else:
break
if os.path.exists(path):
cc.e('failed')
raise RuntimeError('')
else:
cc.i('done')
def makedirs(path, exist_ok=True):
if os.path.exists(path):
if not exist_ok:
raise RuntimeError('path already exists: %s' % path)
else:
return
for i in range(5):
try:
os.makedirs(path)
except:
time.sleep(1)
pass
if not os.path.exists(path):
time.sleep(1)
else:
break
if not os.path.exists(path):
raise RuntimeError('can not create: %s' % path)
def copy_file(s_path, t_path, f_name, force=True):
if isinstance(f_name, str):
f_from = f_name
f_to = f_name
elif isinstance(f_name, tuple):
f_from = f_name[0]
f_to = f_name[1]
else:
raise RuntimeError('utils.copy_file() got invalid param.')
s = os.path.join(s_path, f_from)
t = os.path.join(t_path, f_to)
if os.path.exists(t):
if force:
cc.v(' an exists version found, clean up...')
remove(t)
else:
cc.w(' an exists version found, skip copy.')
return
if not os.path.exists(t_path):
makedirs(t_path)
cc.v('copy [%s]\n -> [%s]' % (s, t))
shutil.copy(s, t)
def copy_ex(s_path, t_path, item_name=None, force=True):
if item_name is None:
s = s_path
t = t_path
else:
if isinstance(item_name, str):
f_from = item_name
f_to = item_name
elif isinstance(item_name, tuple):
f_from = item_name[0]
f_to = item_name[1]
else:
raise RuntimeError('utils.copy_ex() got invalid param.')
s = os.path.join(s_path, f_from)
t = os.path.join(t_path, f_to)
if os.path.exists(t):
if force:
remove(t)
else:
cc.w(t, 'already exists, skip copy.')
return
if os.path.isdir(s):
cc.v('copy [%s]\n -> [%s]' % (s, t))
shutil.copytree(s, t)
else:
if not os.path.exists(t_path):
os.makedirs(t_path)
cc.v('copy [%s]\n -> [%s]' % (s, t))
shutil.copy(s, t)
def update_file(s_path, t_path, f_name):
if isinstance(f_name, str):
f_from = f_name
f_to = f_name
elif isinstance(f_name, tuple):
f_from = f_name[0]
f_to = f_name[1]
else:
raise RuntimeError('utils.update_file() got invalid param.')
s = os.path.join(s_path, f_from)
t = os.path.join(t_path, f_to)
if not os.path.exists(s):
cc.w('try update file `%s` but not exists, skip.' % f_from)
return
# TODO: check file MD5 and update time.
if os.path.exists(t):
remove(t)
if not os.path.exists(t_path):
makedirs(t_path)
cc.v('update [%s]\n -> [%s]' % (s, t))
shutil.copy(os.path.join(s_path, f_from), t)
def ensure_file_exists(filename):
if not os.path.exists(filename):
raise RuntimeError('file not exists: {}'.format(filename))
if not os.path.isfile(filename):
raise RuntimeError('path exists but not a file: {}'.format(filename))
# def root_path():
# return os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
def python_exec():
if not os.path.exists(sys.executable):
raise RuntimeError('Can not locate Python execute file.')
return sys.executable
def msbuild_path():
if cfg.toolchain.msbuild is not None:
return cfg.toolchain.msbuild
# 14.0 = VS2015
# 12.0 = VS2012
# 4.0 = VS2008
chk = ['14.0', '4.0', '12.0']
msp = None
for c in chk:
msp = winreg_read("SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}".format(c), 'MSBuildToolsPath')
if msp is not None:
break
if msp is None:
raise RuntimeError('Can not locate MSBuild.')
msb = os.path.join(msp[0], 'MSBuild.exe')
if not os.path.exists(msb):
raise RuntimeError('Can not locate MSBuild at {}'.format(msp))
cfg.toolchain.msbuild = msb
return msb
def nsis_path():
if cfg.toolchain.nsis is not None:
return cfg.toolchain.nsis
p = winreg_read_wow64_32(r'SOFTWARE\NSIS\Unicode', '')
if p is None:
raise RuntimeError('Can not locate unicode version of NSIS.')
p = os.path.join(p[0], 'makensis.exe')
if not os.path.exists(p):
raise RuntimeError('Can not locate NSIS at {}'.format(p))
cfg.toolchain.nsis = p
return p
def winreg_read(path, key):
try:
hkey = winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ)
value = winreg.QueryValueEx(hkey, key)
except OSError:
return None
return value
def winreg_read_wow64_32(path, key):
try:
hkey = winreg.CreateKeyEx(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ | winreg.KEY_WOW64_32KEY)
value = winreg.QueryValueEx(hkey, key)
except OSError:
return None
return value
def sys_exec(cmd, direct_output=False, output_codec=None):
cc.v(cmd)
# _os = platform.system().lower()
if output_codec is None:
if env.is_win:
output_codec = 'gb2312'
else:
output_codec = 'utf8'
p = None
if env.is_win:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=True)
else:
p = subprocess.Popen(cmd, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=True)
output = ''
f = p.stdout
while True:
line = f.readline()
if 0 == len(line):
break
if direct_output:
cc.o((cc.CR_GRAY, line), end='')
output += line
ret = p.wait()
return (ret, output)
# def msvc_build(sln_file, proj_name, target, platform, force_rebuild):
# msbuild = msbuild_path()
#
# if force_rebuild:
# cmd = '"{}" "{}" "/target:clean" "/property:Configuration={};Platform={}"'.format(msbuild, sln_file, target, platform)
# ret, _ = sys_exec(cmd, direct_output=True)
# cc.v('ret:', ret)
#
# cmd = '"{}" "{}" "/target:{}" "/property:Configuration={};Platform={}"'.format(msbuild, sln_file, proj_name, target, platform)
# ret, _ = sys_exec(cmd, direct_output=True)
# if ret != 0:
# raise RuntimeError('build MSVC project `{}` failed.'.format(proj_name))
def nsis_build(nsi_file, _define=''):
nsis = nsis_path()
cmd = '"{}" /V2 {} /X"SetCompressor /SOLID /FINAL lzma" "{}"'.format(nsis, _define, nsi_file)
ret, _ = sys_exec(cmd, direct_output=True)
if ret != 0:
raise RuntimeError('make installer with nsis failed. [{}]'.format(nsi_file))
def cmake(work_path, target, force_rebuild, cmake_define=''):
# because cmake v2.8 shipped with Ubuntu 14.04LTS, but we need 3.5.
# I copy a v3.5 cmake from CLion.
print(cfg)
if 'cmake' not in cfg.toolchain:
raise RuntimeError('please set `cmake` path.')
print(cfg.toolchain.cmake)
if not os.path.exists(cfg.toolchain.cmake):
raise RuntimeError('`cmake` does not exists, please check your configuration and try again.')
CMAKE = cfg.toolchain.cmake
cc.n('make by cmake', target, sep=': ')
old_p = os.getcwd()
# new_p = os.path.dirname(wscript_file)
# work_path = os.path.join(root_path(), 'cmake-build')
if os.path.exists(work_path):
if force_rebuild:
remove(work_path)
if not os.path.exists(work_path):
makedirs(work_path)
os.chdir(work_path)
if target == 'debug':
target = 'Debug'
else:
target = 'Release'
cmd = '"{}" -DCMAKE_BUILD_TYPE={} {} ..;make'.format(CMAKE, target, cmake_define)
ret, _ = sys_exec(cmd, direct_output=True)
os.chdir(old_p)
if ret != 0:
raise RuntimeError('build with cmake failed, ret={}. [{}]'.format(ret, target))
def strip(filename):
cc.n('strip binary file', filename)
if not os.path.exists(filename):
return False
cmd = 'strip {}'.format(filename)
ret, _ = sys_exec(cmd, direct_output=True)
if ret != 0:
raise RuntimeError('failed to strip binary file [{}], ret={}.'.format(filename, ret))
return True
def make_zip(src_path, to_file):
cc.v('compress folder into .zip...')
n, _ = os.path.splitext(to_file)
# x = os.path.split(to_file)[1].split('.')
p = os.path.dirname(to_file)
shutil.make_archive(os.path.join(p, n), 'zip', src_path)
ensure_file_exists(to_file)
def make_targz(work_path, folder, to_file):
cc.v('compress folder into .tar.gz...')
old_p = os.getcwd()
os.chdir(work_path)
cmd = 'tar zcf "{}" "{}"'.format(to_file, folder)
ret, _ = sys_exec(cmd, direct_output=True)
ensure_file_exists(to_file)
os.chdir(old_p)
if __name__ == '__main__':
# test()
pass