2016-05-30 15:10:57 +02:00
#!/usr/bin/python
2016-10-17 17:15:14 +02:00
import re
import os
import platform
import sys
import getopt
import shutil
import io
import argparse
import json
import urllib2
import subprocess
import textwrap
2016-10-18 12:27:12 +02:00
import traceback
2016-05-30 15:10:57 +02:00
2016-10-18 18:17:09 +02:00
from subprocess import *
2016-10-18 09:58:35 +02:00
BASE_PATH = os . path . abspath ( os . path . dirname ( sys . argv [ 0 ] ) )
2016-10-11 12:29:48 +02:00
proprietary_codecs = False
2016-05-30 15:10:57 +02:00
2016-10-11 13:31:10 +02:00
2016-10-18 09:58:35 +02:00
def main ( ) :
2016-10-17 17:15:14 +02:00
global nw_version
2016-10-18 09:58:35 +02:00
global host_platform
global target_arch
2016-10-17 17:15:14 +02:00
global proprietary_codecs
2016-10-14 17:16:25 +02:00
2016-10-18 09:58:35 +02:00
nw_version = get_latest_stable_nwjs ( )
host_platform = get_host_platform ( )
target_arch = get_host_architecture ( )
2016-10-14 17:16:25 +02:00
2016-10-18 09:58:35 +02:00
try :
args = parse_args ( )
if args . clean :
response = raw_input ( ' Are you sure you want to delete your workspace? (y/n): ' ) . lower ( )
if response == ' y ' :
print ' Cleaning workspace... '
shutil . rmtree ( ' build ' , ignore_errors = True )
else :
print ' Skipping workspace cleaning... '
2016-05-30 15:10:57 +02:00
2016-10-18 09:58:35 +02:00
if args . nw_version :
print ' Setting nw version to ' + args . nw_version
nw_version = " v " + args . nw_version
2016-10-17 17:15:14 +02:00
2016-10-21 20:28:18 +02:00
if args . target_arch :
target_arch = args . target_arch
2016-10-18 09:58:35 +02:00
if target_arch == ' ia32 ' :
target_cpu = ' x86 '
else :
target_cpu = target_arch
2016-10-11 17:08:32 +02:00
2016-10-18 09:58:35 +02:00
proprietary_codecs = args . proprietary_codecs
2016-10-21 20:28:18 +02:00
if proprietary_codecs and platform . system ( ) == ' Windows ' and not ' CYGWIN_NT ' in platform . system ( ) :
2016-10-24 09:16:49 +02:00
print ' Script needs to be executed under CygWin to build FFmpeg \n with proprietary codecs on Windows environments, \n read https://github.com/iteufel/nwjs-ffmpeg-prebuilt/blob/master/guides/build_windows.md \n Exiting... '
2016-10-21 20:28:18 +02:00
sys . exit ( 1 )
2016-10-18 09:58:35 +02:00
2016-10-21 20:28:18 +02:00
print ' Building ffmpeg for {0} on {1} for {2} , proprietary_codecs = {3} ' . format ( nw_version , host_platform , target_cpu , proprietary_codecs )
2016-10-18 09:58:35 +02:00
create_build_directory ( )
clean_output_directory ( )
2016-10-17 17:15:14 +02:00
2016-10-18 09:58:35 +02:00
os . chdir ( ' build ' )
2016-10-17 17:15:14 +02:00
2016-10-18 09:58:35 +02:00
setup_chromium_depot_tools ( )
clone_chromium_source_code ( )
os . chdir ( ' src ' )
reset_chromium_src_to_nw_version ( )
generate_build_and_deps_files ( )
install_build_deps ( )
print ' Syncing with gclient... '
os . system ( ' gclient sync --no-history ' )
check_build_with_proprietary_codecs ( )
2016-10-23 19:44:35 +02:00
print ' Generating ninja files... '
subprocess . check_call ( ' gn gen //out/nw " --args=is_debug=false is_component_ffmpeg=true target_cpu= \\ \" %s \\ \" is_official_build=true ffmpeg_branding= \\ \" Chrome \\ \" " ' % target_cpu , shell = True )
2016-10-18 09:58:35 +02:00
print ' Starting ninja for building ffmpeg... '
subprocess . check_call ( ' ninja -C out/nw ffmpeg ' , shell = True )
2016-10-18 18:17:09 +02:00
except KeyboardInterrupt :
print " \n \n Shutdown requested... exiting "
2016-10-23 21:05:44 +02:00
except Exception :
print traceback . format_exc ( )
sys . exit ( 1 )
2016-10-18 09:58:35 +02:00
def parse_args ( ) :
parser = argparse . ArgumentParser ( description = ' ffmpeg builder script. ' )
parser . add_argument ( ' -c ' , ' --clean ' , help = ' Clean the workspace, removes downloaded source code ' , required = False , action = ' store_true ' )
parser . add_argument ( ' -nw ' , ' --nw_version ' , help = ' Build ffmpeg for the specified Nw.js version ' , required = False )
parser . add_argument ( ' -ta ' , ' --target_arch ' , help = ' Target architecture, ia32, x64 ' , required = False )
parser . add_argument ( ' -pc ' , ' --proprietary_codecs ' , help = ' Build ffmpeg with proprietary codecs ' , required = False , action = ' store_true ' )
return parser . parse_args ( )
2016-10-17 17:15:14 +02:00
def grep_dep ( reg , repo , dir , deps_str ) :
pat = re . compile ( reg )
found = re . search ( pat , deps_str )
if found is None :
return None
head = found . group ( 1 )
return textwrap . dedent ( '''
' %s ' :
( Var ( \" chromium_git \" )) + ' %s @ %s ' ,
''' ) % (dir, repo, head)
def get_host_platform ( ) :
2016-10-23 21:00:59 +02:00
if platform . system ( ) == ' Windows ' or ' CYGWIN_NT ' in platform . system ( ) :
2016-10-17 17:15:14 +02:00
host_platform = ' win '
2016-10-23 21:00:59 +02:00
elif platform . system ( ) == ' Linux ' :
2016-10-17 17:15:14 +02:00
host_platform = ' linux '
2016-10-23 21:00:59 +02:00
elif platform . system ( ) == ' Darwin ' :
2016-10-17 17:15:14 +02:00
host_platform = ' mac '
return host_platform
def get_host_architecture ( ) :
if re . match ( r ' i.86 ' , platform . machine ( ) ) :
host_arch = ' ia32 '
elif platform . machine ( ) == ' x86_64 ' or platform . machine ( ) == ' AMD64 ' :
host_arch = ' x64 '
elif platform . machine ( ) == ' aarch64 ' :
host_arch = ' arm64 '
elif platform . machine ( ) . startswith ( ' arm ' ) :
host_arch = ' arm '
else :
print ' Unexpected host machine architecture, exiting... '
sys . exit ( 1 )
return host_arch
def get_latest_stable_nwjs ( ) :
# We always build ffmpeg for the latest stable
nwjs_io_url = ' http://nwjs.io/versions.json '
try :
versions = json . load ( urllib2 . urlopen ( nwjs_io_url ) )
nw_version = versions [ ' stable ' ]
except URLError :
nw_version = ' v0.18.0 '
print ' Error fetching ' + nwjs_io_url + ' URL, fall back to NW.js version ' + nw_version
return nw_version
def create_build_directory ( ) :
try :
os . mkdir ( ' build ' )
except OSError :
print ' Build directory is already created, skipping... '
def clean_output_directory ( ) :
print ' Cleaning output directory... '
shutil . rmtree ( ' build/src/out ' , ignore_errors = True )
def setup_chromium_depot_tools ( ) :
if not os . path . isdir ( os . getcwd ( ) + ' /depot_tools/.git ' ) :
print ' Cloning Chromium depot tools in {0} ... ' . format ( os . getcwd ( ) )
os . system ( ' git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git ' )
sys . path . append ( os . getcwd ( ) + ' /depot_tools ' )
# fix for gclient not found, seems like sys.path.append does not work but
# path is added
os . environ [ " PATH " ] + = os . pathsep + os . getcwd ( ) + " /depot_tools "
if platform . system ( ) == ' Windows ' or ' CYGWIN_NT ' in platform . system ( ) :
os . environ [ " DEPOT_TOOLS_WIN_TOOLCHAIN " ] = ' 0 '
print ' Creating .gclient file... '
subprocess . check_call ( ' gclient config --unmanaged --name=src https://github.com/nwjs/chromium.src.git@tags/nw- {0} ' . format ( nw_version ) , shell = True )
def clone_chromium_source_code ( ) :
print ' Cloning Chromium source code for nw- {0} in {1} ' . format ( nw_version , os . getcwd ( ) )
os . system ( ' git clone --depth=1 -b nw- {0} --single-branch {1} src ' . format (
nw_version , ' https://github.com/nwjs/chromium.src.git ' ) )
def reset_chromium_src_to_nw_version ( ) :
print ' Hard source code reset to nw {0} specified version ' . format ( nw_version )
os . system ( ' git reset --hard tags/nw- {0} ' . format ( nw_version ) )
def get_min_deps ( deps_str ) :
# deps
deps_list = {
' buildtools ' : {
' reg ' : ur ' buildtools \ .git@(.+) \' ' ,
' repo ' : ' /chromium/buildtools.git ' ,
' path ' : ' src/buildtools '
} ,
' gyp ' : {
' reg ' : ur ' gyp \ .git@(.+) \' ' ,
' repo ' : ' /external/gyp.git ' ,
' path ' : ' src/tools/gyp '
} ,
' patched-yasm ' : {
' reg ' : ur ' patched-yasm \ .git@(.+) \' ' ,
' repo ' : ' /chromium/deps/yasm/patched-yasm.git ' ,
' path ' : ' src/third_party/yasm/source/patched-yasm '
} ,
' ffmpeg ' : {
' reg ' : ur ' ffmpeg \ .git@(.+) \' ' ,
' repo ' : ' /chromium/third_party/ffmpeg ' ,
' path ' : ' src/third_party/ffmpeg '
} ,
}
min_deps_list = [ ]
for k , v in deps_list . items ( ) :
dep = grep_dep ( v [ ' reg ' ] , v [ ' repo ' ] , v [ ' path ' ] , deps_str )
if dep is None :
raise Exception ( " ` %s ` is not found in DEPS " % k )
min_deps_list . append ( dep )
return textwrap . dedent ( '''
deps = {
% s
}
''' ) % " \n " .join(min_deps_list)
def get_min_vars ( ) :
# vars
# copied from DEPS
return textwrap . dedent ( '''
vars = {
' chromium_git ' :
' https://chromium.googlesource.com ' ,
}
''' )
def get_min_hooks ( ) :
# hooks
# copied from DEPS
return textwrap . dedent ( '''
hooks = [
{
' action ' : [
' python ' ,
' src/build/linux/sysroot_scripts/install-sysroot.py ' ,
' --running-as-hook '
] ,
' pattern ' :
' . ' ,
' name ' :
' sysroot '
} ,
{
' action ' : [
' python ' ,
' src/build/mac_toolchain.py '
] ,
' pattern ' :
' . ' ,
' name ' :
' mac_toolchain '
} ,
{
' action ' : [
' python ' ,
' src/tools/clang/scripts/update.py ' ,
' --if-needed '
] ,
' pattern ' :
' . ' ,
' name ' :
' clang '
} ,
{
' action ' : [
' download_from_google_storage ' ,
' --no_resume ' ,
' --platform=win32 ' ,
' --no_auth ' ,
' --bucket ' ,
' chromium-gn ' ,
' -s ' ,
' src/buildtools/win/gn.exe.sha1 '
] ,
' pattern ' :
' . ' ,
' name ' :
' gn_win '
} ,
{
' action ' : [
' download_from_google_storage ' ,
' --no_resume ' ,
' --platform=darwin ' ,
' --no_auth ' ,
' --bucket ' ,
' chromium-gn ' ,
' -s ' ,
' src/buildtools/mac/gn.sha1 '
] ,
' pattern ' :
' . ' ,
' name ' :
' gn_mac '
} ,
{
' action ' : [
' download_from_google_storage ' ,
' --no_resume ' ,
' --platform=linux* ' ,
' --no_auth ' ,
' --bucket ' ,
' chromium-gn ' ,
' -s ' ,
' src/buildtools/linux64/gn.sha1 '
] ,
' pattern ' :
' . ' ,
' name ' :
' gn_linux64 '
} ,
{
' action ' : [
' download_from_google_storage ' ,
' --no_resume ' ,
' --platform=darwin ' ,
' --no_auth ' ,
' --bucket ' ,
' chromium-libcpp ' ,
' -s ' ,
' src/third_party/libc++-static/libc++.a.sha1 '
] ,
' pattern ' :
' . ' ,
' name ' :
' libcpp_mac '
} ,
]
''' )
def install_build_deps ( ) :
# install linux dependencies
if platform . system ( ) == ' Linux ' and not os . path . isfile ( ' buid_deps.ok ' ) :
os . system ( ' ./build/install-build-deps.sh --no-prompt --no-nacl --no-chromeos-fonts --no-syms ' )
with io . FileIO ( ' buid_deps.ok ' , ' w ' ) as file :
file . write ( ' Build dependencies already installed ' )
def delete_file ( file_name ) :
if os . path . exists ( file_name ) :
try :
os . remove ( file_name )
except OSError as e :
print ( " Error: %s - %s . " % ( e . filename , e . strerror ) )
def generate_build_and_deps_files ( ) :
print ' Cleaning previous DEPS and BUILD.gn backup files... '
delete_file ( " DEPS.bak " )
delete_file ( " BUILD.gn.bak " )
with open ( ' DEPS ' , ' r ' ) as f :
deps_str = f . read ( )
print ' Backing up and overwriting DEPS... '
shutil . move ( ' DEPS ' , ' DEPS.bak ' )
with open ( ' DEPS ' , ' w ' ) as f :
f . write ( " %s \n %s \n %s " % ( get_min_vars ( ) , get_min_deps ( deps_str ) , get_min_hooks ( ) ) )
print ' Backing up and overwriting BUILD.gn... '
shutil . move ( ' BUILD.gn ' , ' BUILD.gn.bak ' )
BUILD_gn = textwrap . dedent ( '''
group ( " dummy " ) {
deps = [
" //third_party/ffmpeg "
]
}
''' )
with open ( ' BUILD.gn ' , ' w ' ) as f :
f . write ( BUILD_gn )
2016-10-24 09:27:37 +02:00
def cygwin_linking_setup ( ) :
if ' CYGWIN_NT ' in platform . system ( ) :
if os . path . isfile ( ' /usr/bin/link.exe ' ) :
print ' Overriding CygWin linker with MSVC linker... '
shutil . move ( ' /usr/bin/link.exe ' , ' /usr/bin/link.exe.1 ' )
if not os . path . isfile ( ' /usr/local/bin/cygwin-wrapper ' ) :
print ' Copying Cygwin wrapper... '
shutil . copy ( os . getcwd ( ) + ' /chromium/scripts/cygwin-wrapper ' , ' /usr/local/bin/cygwin-wrapper ' )
2016-10-17 18:40:44 +02:00
def check_build_with_proprietary_codecs ( ) :
2016-10-17 17:15:14 +02:00
# going to ffmpeg folder
os . chdir ( ' third_party/ffmpeg ' )
2016-10-11 12:29:48 +02:00
2016-10-17 18:40:44 +02:00
if proprietary_codecs :
2016-10-21 20:28:18 +02:00
print ' Building ffmpeg with proprietary codecs... '
2016-10-24 17:40:43 +02:00
if not os . path . isfile ( ' build_ffmpeg_proprietary_codecs.patch ' ) :
2016-10-23 21:10:38 +02:00
print ' Applying codecs patch with ac3 for {0} ... ' . format ( host_platform )
shutil . copy ( BASE_PATH + ' /patch/ ' + host_platform + ' /build_ffmpeg_proprietary_codecs.patch ' , os . getcwd ( ) )
2016-10-17 18:40:44 +02:00
# apply codecs patch
os . system ( ' git apply --ignore-space-change --ignore-whitespace build_ffmpeg_proprietary_codecs.patch ' )
2016-10-24 09:27:37 +02:00
cygwin_linking_setup ( )
2016-10-21 20:28:18 +02:00
print ' Starting build... '
2016-10-17 18:40:44 +02:00
# build ffmpeg
subprocess . check_call ( ' ./chromium/scripts/build_ffmpeg.py {0} {1} ' . format ( host_platform , target_arch ) , shell = True )
# copy the new generated ffmpeg config
print ' Copying new ffmpeg configuration... '
subprocess . call ( ' ./chromium/scripts/copy_config.sh ' , shell = True )
print ' Creating a GYP include file for building FFmpeg from source... '
# generate the ffmpeg configuration
subprocess . check_call ( ' ./chromium/scripts/generate_gyp.py ' , shell = True )
2016-10-23 21:06:47 +02:00
2016-10-24 09:16:49 +02:00
if ' CYGWIN_NT ' in platform . system ( ) :
2016-10-23 21:06:47 +02:00
print ' Applying fix for error LNK2001: unresolved external symbol _ff_w64_guid_data '
fix_external_symbol_ff_w64_guid_data ( )
2016-10-17 18:40:44 +02:00
else :
2016-10-24 17:40:43 +02:00
if os . path . isfile ( ' build_ffmpeg_proprietary_codecs.patch ' ) :
2016-10-17 18:40:44 +02:00
print ' Restoring ffmpeg configuration to defaults... '
os . system ( ' git clean -df ' )
os . system ( ' git checkout -- . ' )
2016-10-11 12:29:48 +02:00
2016-10-17 17:15:14 +02:00
# back to src
os . chdir ( ' ../.. ' )
2016-10-21 20:28:18 +02:00
def replace_in_file ( file_name , search_string , replace_string ) :
filedata = None
with open ( file_name , ' r ' ) as file :
filedata = file . read ( )
filedata = filedata . replace ( search_string , replace_string )
with open ( file_name , ' w ' ) as file :
file . write ( filedata )
def fix_external_symbol_ff_w64_guid_data ( ) :
# https://bugs.chromium.org/p/chromium/issues/detail?id=264459
shutil . copyfile ( ' ffmpeg_generated.gni ' , ' ffmpeg_generated.gni.bak ' )
shutil . copyfile ( ' ffmpeg_generated.gypi ' , ' ffmpeg_generated.gypi.bak ' )
replace = ''' " libavformat/vorbiscomment.c " ,
" libavformat/w64.c " , '''
replace_in_file ( ' ffmpeg_generated.gni ' , ' " libavformat/vorbiscomment.c " , ' , replace )
replace = ''' ' libavformat/vorbiscomment.c ' ,
' libavformat/w64.c ' , '''
replace_in_file ( ' ffmpeg_generated.gypi ' , " ' libavformat/vorbiscomment.c ' , " , replace )
2016-10-17 18:40:44 +02:00
2016-10-18 09:58:35 +02:00
if __name__ == ' __main__ ' :
sys . exit ( main ( ) )