2014-07-30 15:44:15 +08:00
#!/usr/bin/python
# ----------------------------------------------------------------------------
# generate the prebuilt libs of engine
#
# Copyright 2014 (C) zhangbin
#
# License: MIT
# ----------------------------------------------------------------------------
'''
Generate the prebuilt libs of engine
'''
import os
import subprocess
import shutil
import sys
import excopy
import json
from argparse import ArgumentParser
if sys . platform == ' win32 ' :
import _winreg
2014-07-31 16:07:15 +08:00
TESTS_PROJ_PATH = " tests/lua-tests "
ANDROID_SO_PATH = " project/proj.android/libs "
ANDROID_A_PATH = " project/proj.android/obj/local "
MK_PATH = " project/proj.android/jni/Application.mk "
2014-07-30 15:44:15 +08:00
CONSOLE_PATH = " tools/cocos2d-console/bin "
def os_is_win32 ( ) :
return sys . platform == ' win32 '
def os_is_mac ( ) :
return sys . platform == ' darwin '
def run_shell ( cmd , cwd = None ) :
p = subprocess . Popen ( cmd , shell = True , cwd = cwd )
p . wait ( )
if p . returncode :
raise subprocess . CalledProcessError ( returncode = p . returncode , cmd = cmd )
return p . returncode
class Generator ( object ) :
XCODE_CMD_FMT = " xcodebuild -project \" %s \" -configuration Release -target \" %s \" %s CONFIGURATION_BUILD_DIR= %s "
CONFIG_FILE = " build_config.json "
KEY_XCODE_PROJ_INFO = " xcode_proj_info "
KEY_WIN32_PROJ_INFO = " win32_proj_info "
KEY_OUTPUT_DIR = " outputdir "
KEY_TARGETS = " targets "
def __init__ ( self , args ) :
self . need_clean = args . need_clean
2014-08-08 15:21:24 +08:00
self . disable_strip = args . disable_strip
2014-07-30 15:44:15 +08:00
self . use_incredibuild = args . use_incredibuild
self . tool_dir = os . path . realpath ( os . path . dirname ( __file__ ) )
self . no_android = args . no_android
self . engine_dir = os . path . join ( self . tool_dir , os . path . pardir , os . path . pardir )
self . load_config ( )
def load_config ( self ) :
cfg_json = os . path . join ( self . tool_dir , Generator . CONFIG_FILE )
f = open ( cfg_json )
cfg_info = json . load ( f )
f . close ( )
self . xcode_proj_info = cfg_info [ Generator . KEY_XCODE_PROJ_INFO ]
self . win32_proj_info = cfg_info [ Generator . KEY_WIN32_PROJ_INFO ]
def modify_mk ( self , mk_file ) :
if os . path . isfile ( mk_file ) :
file_obj = open ( mk_file , " a " )
file_obj . write ( " \n APP_ABI :=armeabi armeabi-v7a \n " )
file_obj . close ( )
def build_android ( self ) :
2014-07-31 16:07:15 +08:00
# build .a for android
2014-07-30 15:44:15 +08:00
console_dir = os . path . join ( self . engine_dir , CONSOLE_PATH )
cmd_path = os . path . join ( console_dir , " cocos " )
2014-07-31 16:07:15 +08:00
proj_path = os . path . join ( self . engine_dir , TESTS_PROJ_PATH )
2014-07-30 15:44:15 +08:00
# Add multi ABI in Application.mk
mk_file = os . path . join ( proj_path , MK_PATH )
2014-07-31 16:07:15 +08:00
f = open ( mk_file )
file_content = f . read ( )
f . close ( )
2014-07-30 15:44:15 +08:00
self . modify_mk ( mk_file )
# build it
build_cmd = " %s compile -s %s -p android --ndk-mode release -j 4 " % ( cmd_path , proj_path )
run_shell ( build_cmd )
2014-07-31 16:07:15 +08:00
f = open ( mk_file , " w " )
f . write ( file_content )
f . close ( )
2014-07-30 15:44:15 +08:00
# copy .a to prebuilt dir
obj_dir = os . path . join ( proj_path , ANDROID_A_PATH )
prebuilt_dir = os . path . join ( self . tool_dir , " prebuilt " , " android " )
copy_cfg = {
" from " : obj_dir ,
" to " : prebuilt_dir ,
" include " : [
" *.a$ "
]
}
excopy . copy_files_with_config ( copy_cfg , obj_dir , prebuilt_dir )
2014-08-08 15:21:24 +08:00
if not self . disable_strip :
2014-07-30 15:44:15 +08:00
# strip the android libs
ndk_root = os . environ [ " NDK_ROOT " ]
if os_is_win32 ( ) :
if self . is_32bit_windows ( ) :
2014-07-31 16:07:15 +08:00
bit_str = " "
2014-07-30 15:44:15 +08:00
else :
2014-07-31 16:07:15 +08:00
bit_str = " -x86_64 "
2014-07-30 15:44:15 +08:00
2014-07-31 16:07:15 +08:00
sys_folder_name = " windows %s " % bit_str
2014-07-30 15:44:15 +08:00
elif os_is_mac ( ) :
sys_folder_name = " darwin-x86_64 "
strip_cmd_path = os . path . join ( ndk_root , " toolchains/arm-linux-androideabi-4.8/prebuilt/ %s /arm-linux-androideabi/bin/strip " % sys_folder_name )
if os . path . exists ( strip_cmd_path ) :
strip_cmd = " %s -S %s /armeabi*/*.a " % ( strip_cmd_path , prebuilt_dir )
run_shell ( strip_cmd )
def get_required_vs_version ( self , proj_file ) :
# get the VS version required by the project
import re
file_obj = open ( proj_file )
pattern = re . compile ( r " ^# Visual Studio.+( \ d {4} ) " )
num = None
for line in file_obj :
match = pattern . match ( line )
if match is not None :
num = match . group ( 1 )
break
if num is not None :
if num == " 2012 " :
ret = " 11.0 "
elif num == " 2013 " :
ret = " 12.0 "
else :
ret = None
else :
ret = None
return ret
def get_vs_cmd_path ( self , require_version ) :
# find the VS in register, if system is 64bit, should find vs in both 32bit & 64bit register
if self . is_32bit_windows ( ) :
reg_flag_list = [ _winreg . KEY_WOW64_32KEY ]
else :
reg_flag_list = [ _winreg . KEY_WOW64_64KEY , _winreg . KEY_WOW64_32KEY ]
needUpgrade = False
vsPath = None
try :
for reg_flag in reg_flag_list :
print ( " find vs in reg : %s " % ( " 32bit " if reg_flag == _winreg . KEY_WOW64_32KEY else " 64bit " ) )
vs = _winreg . OpenKey (
_winreg . HKEY_LOCAL_MACHINE ,
r " SOFTWARE \ Microsoft \ VisualStudio " ,
0 ,
_winreg . KEY_READ | reg_flag
)
try :
i = 0
while True :
try :
# enum the keys in vs reg
version = _winreg . EnumKey ( vs , i )
find_ver = float ( version )
# find the vs which version >= required version
if find_ver > = float ( require_version ) :
key = _winreg . OpenKey ( vs , r " SxS \ VS7 " )
vsPath , type = _winreg . QueryValueEx ( key , version )
if os . path . exists ( vsPath ) :
if float ( version ) > float ( require_version ) :
needUpgrade = True
break
else :
vsPath = None
except :
continue
finally :
i + = 1
except :
pass
# if find one right vs, break
if vsPath is not None :
break
except WindowsError as e :
message = " Visual Studio wasn ' t installed "
print ( e )
raise Exception ( message )
commandPath = os . path . join ( vsPath , " Common7 " , " IDE " , " devenv " )
return ( needUpgrade , commandPath )
def is_32bit_windows ( self ) :
arch = os . environ [ ' PROCESSOR_ARCHITECTURE ' ] . lower ( )
archw = os . environ . has_key ( " PROCESSOR_ARCHITEW6432 " )
return ( arch == " x86 " and not archw )
def build_win32_proj ( self , cmd_path , sln_path , proj_name , mode ) :
build_cmd = " " . join ( [
" \" %s \" " % cmd_path ,
" \" %s \" " % sln_path ,
" / %s \" Release|Win32 \" " % mode ,
" /Project \" %s \" " % proj_name
] )
run_shell ( build_cmd )
def build_win32 ( self ) :
print ( " Building Win32 " )
for key in self . win32_proj_info . keys ( ) :
output_dir = self . win32_proj_info [ key ] [ Generator . KEY_OUTPUT_DIR ]
proj_path = os . path . join ( self . engine_dir , key )
require_vs_version = self . get_required_vs_version ( proj_path )
needUpgrade , vs_command = self . get_vs_cmd_path ( require_vs_version )
# get the build folder & win32 output folder
build_folder_path = os . path . join ( os . path . dirname ( proj_path ) , " Release.win32 " )
if os . path . exists ( build_folder_path ) :
shutil . rmtree ( build_folder_path )
os . makedirs ( build_folder_path )
win32_output_dir = os . path . join ( self . tool_dir , output_dir )
if os . path . exists ( win32_output_dir ) :
shutil . rmtree ( win32_output_dir )
os . makedirs ( win32_output_dir )
# upgrade projects
if needUpgrade :
commandUpgrade = ' ' . join ( [
" \" %s \" " % vs_command ,
" \" %s \" " % proj_path ,
" /Upgrade "
] )
run_shell ( commandUpgrade )
if self . use_incredibuild :
# use incredibuild, build whole sln
build_cmd = " " . join ( [
" BuildConsole " ,
" %s " % proj_path ,
" /build " ,
" /cfg= \" Release|Win32 \" "
] )
run_shell ( build_cmd )
if not self . use_incredibuild :
# build the projects
for proj_name in self . win32_proj_info [ key ] [ Generator . KEY_TARGETS ] :
self . build_win32_proj ( vs_command , proj_path , proj_name , " build " )
lib_file_path = os . path . join ( build_folder_path , " %s .lib " % proj_name )
if not os . path . exists ( lib_file_path ) :
# if the lib is not generated, rebuild the project
self . build_win32_proj ( vs_command , proj_path , proj_name , " rebuild " )
if not os . path . exists ( lib_file_path ) :
raise Exception ( " Library %s not generated as expected! " % lib_file_path )
# copy the libs into prebuilt dir
for file_name in os . listdir ( build_folder_path ) :
file_path = os . path . join ( build_folder_path , file_name )
shutil . copy ( file_path , win32_output_dir )
print ( " Win32 build succeeded. " )
def build_ios_mac ( self ) :
for key in self . xcode_proj_info . keys ( ) :
output_dir = self . xcode_proj_info [ key ] [ Generator . KEY_OUTPUT_DIR ]
proj_path = os . path . join ( self . engine_dir , key )
ios_out_dir = os . path . join ( self . tool_dir , output_dir , " ios " )
mac_out_dir = os . path . join ( self . tool_dir , output_dir , " mac " )
ios_sim_libs_dir = os . path . join ( ios_out_dir , " simulator " )
ios_dev_libs_dir = os . path . join ( ios_out_dir , " device " )
for target in self . xcode_proj_info [ key ] [ Generator . KEY_TARGETS ] :
build_cmd = Generator . XCODE_CMD_FMT % ( proj_path , " %s iOS " % target , " -sdk iphonesimulator " , ios_sim_libs_dir )
run_shell ( build_cmd , self . tool_dir )
build_cmd = Generator . XCODE_CMD_FMT % ( proj_path , " %s iOS " % target , " -sdk iphoneos " , ios_dev_libs_dir )
run_shell ( build_cmd , self . tool_dir )
build_cmd = Generator . XCODE_CMD_FMT % ( proj_path , " %s Mac " % target , " " , mac_out_dir )
run_shell ( build_cmd , self . tool_dir )
# generate fat libs for iOS
for lib in os . listdir ( ios_sim_libs_dir ) :
sim_lib = os . path . join ( ios_sim_libs_dir , lib )
dev_lib = os . path . join ( ios_dev_libs_dir , lib )
output_lib = os . path . join ( ios_out_dir , lib )
lipo_cmd = " lipo -create -output \" %s \" \" %s \" \" %s \" " % ( output_lib , sim_lib , dev_lib )
run_shell ( lipo_cmd )
# remove the simulator & device libs in iOS
shutil . rmtree ( ios_sim_libs_dir )
shutil . rmtree ( ios_dev_libs_dir )
2014-08-08 15:21:24 +08:00
if not self . disable_strip :
2014-07-30 15:44:15 +08:00
# strip the libs
ios_strip_cmd = " xcrun -sdk iphoneos strip -S %s /*.a " % ios_out_dir
run_shell ( ios_strip_cmd )
mac_strip_cmd = " xcrun strip -S %s /*.a " % mac_out_dir
run_shell ( mac_strip_cmd )
def build_all_libs ( self ) :
if os_is_mac ( ) :
# build for iOS & Mac
self . build_ios_mac ( )
if os_is_win32 ( ) :
# build for win32
self . build_win32 ( )
if not self . no_android :
self . build_android ( )
def do_generate ( self ) :
output_dir = os . path . join ( self . tool_dir , " prebuilt " )
if self . need_clean and os . path . exists ( output_dir ) :
shutil . rmtree ( output_dir )
self . build_all_libs ( )
if __name__ == " __main__ " :
parser = ArgumentParser ( description = " Generate prebuilt engine for Cocos Engine. " )
parser . add_argument ( ' -c ' , dest = ' need_clean ' , action = " store_true " , help = ' Remove the \" prebuilt \" directory first. ' )
parser . add_argument ( ' -n ' , " --no-android " , dest = ' no_android ' , action = " store_true " , help = ' Not build android libs. ' )
2014-08-08 15:21:24 +08:00
parser . add_argument ( ' -d ' , " --disable-strip " , dest = ' disable_strip ' , action = " store_true " , help = ' Disable the strip of the generated libs. ' )
2014-07-30 15:44:15 +08:00
parser . add_argument ( ' -i ' , " --incredibuild " , dest = ' use_incredibuild ' , action = " store_true " , help = ' Use incredibuild to build win32 projects. Only available on windows. ' )
( args , unknown ) = parser . parse_known_args ( )
if len ( unknown ) > 0 :
print ( " unknown arguments: %s " % unknown )
gen_obj = Generator ( args )
gen_obj . do_generate ( )