teleport/build/builder/core/utils.py

442 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env python
# -*- coding: utf8 -*-
import os
import platform
import shutil
import subprocess
import sys
import time
from . import colorconsole as cc
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
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.')
# cc.v(EXTENSION_SUFFIXES)
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.msbuild is not None:
return cfg.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.msbuild = msb
return msb
def nsis_path():
if cfg.nsis is not None:
return cfg.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.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):
# 注意output_codec在windows默认为gb2312其他平台默认utf8
_os = platform.system().lower()
if output_codec is None:
if _os == 'windows':
output_codec = 'gb2312'
else:
output_codec = 'utf8'
p = None
if _os == 'windows':
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.v(line.decode(output_codec))
# cc.d(line, end='')
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)
def fix_extension_files(s_path, t_path):
cc.n('\nfix extension files...')
# 遍历s_path目录下的所有Python扩展文件动态库并将其移动到t_path目录下同时改名。
# 例如, s_path/abc/def.pyd -> t_path/abc.def.pyd
s_path = os.path.abspath(s_path)
t_path = os.path.abspath(t_path)
ext = extension_suffixes()
s_path_len = len(s_path)
def _fix_(s_path, t_path, sub_path):
for parent, dir_list, file_list in os.walk(sub_path):
for d in dir_list:
_fix_(s_path, t_path, os.path.join(parent, d))
for filename in file_list:
_, e = os.path.splitext(filename)
if e in ext:
f_from = os.path.join(parent, filename)
f_to = f_from[s_path_len + 1:]
f_to = f_to.replace('\\', '.')
f_to = f_to.replace('/', '.')
f_to = os.path.join(t_path, f_to)
cc.v('move: ', f_from, '\n -> ', f_to)
shutil.move(f_from, f_to)
_fix_(s_path, t_path, s_path)
if __name__ == '__main__':
# test()
pass