# Copyright (c) 2016 CEF Python, see the Authors file. # All rights reserved. Licensed under BSD 3-clause license. # Project website: https://github.com/cztomczak/cefpython """ Prepares CEF binaries and libraries for work with the build.py tool. Option 1 is to build CEF from sources with the CEF Python patches applied using the --build-cef flag. Building CEF from sources is supported only on 64-bit systems. 32-bit is also built on 64-bit using cross-compiling. Note that building CEF from sources was last tested with v56 on Linux and with v50 on Windows, so if there are issues report them on the Forum. Option 2 is to use CEF binaries from Spotify Automated Builds using the --prebuilt-cef flag. In such case check the cefpython/src/version/ directory to know which version of CEF to download from Spotify: http://opensource.spotify.com/cefbuilds/index.html Download and extract it so that for example you have such a directory: cefpython/build/cef_binary_3.2883.1553.g80bd606_windows32/ . This tool generates CEF binaries and libraries that are ready for work with cefpython, with the build.py script. When automate.py tool completes job you should see a new subdirectory in the build/ directory, for example: cefpython/build/cef55_3.2883.1553.g80bd606_win32/ . Usage: automate.py (--prebuilt-cef | --build-cef) [--x86 X86] [--fast-build FAST_BUILD] [--force-chromium-update FORCE_CHROMIUM_UPDATE] [--no-cef-update NO_CEF_UPDATE] [--cef-branch BRANCH] [--cef-commit COMMIT] [--build-dir BUILD_DIR] [--cef-build-dir CEF_BUILD_DIR] [--ninja-jobs JOBS] [--gyp-generators GENERATORS] [--gyp-msvs-version MSVS] automate.py (-h | --help) [type -h to show full description for options] Options: -h --help Show this help message. --prebuilt-cef Whether to use prebuilt CEF binaries. Prebuilt binaries for Linux are built on Ubuntu. --build-cef Whether to build CEF from sources with the cefpython patches applied. --x86 Build 32-bit CEF on 64-bit system --fast-build Fast build with is_official_build=False --force-chromium-update Force Chromium update (gclient sync etc). --no-cef-update Do not update CEF sources (by default both cef/ directories are deleted on every run). --cef-branch= CEF branch. Defaults to CHROME_VERSION_BUILD from "src/version/cef_version_{platform}.h". --cef-commit= CEF revision. Defaults to CEF_COMMIT_HASH from "src/version/cef_version_{platform}.h". --build-dir= Build directory. --cef-build-dir= CEF build directory. By default same as --build-dir. --ninja-jobs= How many CEF jobs to run in parallel. To speed up building set it to number of cores in your CPU. By default set to cpu_count / 2. --gyp-generators= Set GYP_GENERATORS [default: ninja]. --gyp-msvs-version= Set GYP_MSVS_VERSION. """ from common import * import os import sys import shlex import subprocess import platform import docopt import stat import glob import shutil import multiprocessing from collections import OrderedDict from setuptools.msvc import msvc9_query_vcvarsall # Constants CEF_GIT_URL = "https://bitbucket.org/chromiumembedded/cef.git" RUNTIME_MT = "MT" RUNTIME_MD = "MD" class Options(object): """Options from command-line and internal options.""" # From command-line prebuilt_cef = False build_cef = False x86 = False fast_build = False force_chromium_update = False no_cef_update = False cef_branch = "" cef_commit = "" cef_version = "" build_dir = "" cef_build_dir = "" ninja_jobs = None gyp_generators = "ninja" # Even though CEF uses now GN, still some GYP gyp_msvs_version = "" # env variables are being used. # Internal options depot_tools_dir = "" tools_dir = "" cefpython_dir = "" binary_distrib = "" release_build = True build_type = "" # Will be set according to "release_build" value cef_binary = "" def main(): """Main entry point.""" if len(sys.argv) = 2704: Options.gyp_msvs_version = "2015" else: Options.gyp_msvs_version = "2013" # --build-dir if Options.build_dir: Options.build_dir = os.path.realpath(Options.build_dir) else: Options.build_dir = os.path.join(Options.cefpython_dir, "build") if " " in Options.build_dir: print("[automate.py] ERROR: Build dir cannot contain spaces") print(">> " + Options.build_dir) sys.exit(1) if not os.path.exists(Options.build_dir): os.makedirs(Options.build_dir) # --cef-build-dir if Options.cef_build_dir: Options.cef_build_dir = os.path.realpath(Options.cef_build_dir) else: Options.cef_build_dir = Options.build_dir if " " in Options.cef_build_dir: print("[automate.py] ERROR: CEF build dir cannot contain spaces") print(">> " + Options.cef_build_dir) sys.exit(1) if not os.path.exists(Options.cef_build_dir): os.makedirs(Options.cef_build_dir) # --depot-tools-dir Options.depot_tools_dir = os.path.join(Options.cef_build_dir, "depot_tools") # binary_distrib Options.binary_distrib = os.path.join(Options.cef_build_dir, "chromium", "src", "cef", "binary_distrib") # build_type Options.build_type = "Release" if Options.release_build else "Debug" # ninja_jobs # cpu_count() returns number of CPU threads, not CPU cores. # On i5 with 2 cores and 4 cpu threads the default of 4 ninja # jobs slows down computer significantly. if not Options.ninja_jobs: Options.ninja_jobs = int(multiprocessing.cpu_count() / 2) if Options.ninja_jobs cefclient_mac.mm:22:29: error: property 'mainMenu' not found if MAC: # Build only cefsimple command.extend(["ninja", "cefsimple"]) else: command.extend(["ninja", "cefclient", "cefsimple", "ceftests"]) run_command(command, build_cefclient_dir) print("[automate.py] OK") assert os.path.exists(cefclient_exe) # Build libcef_dll_wrapper libs if WINDOWS: build_all_wrapper_libraries_windows() elif MAC: build_wrapper_library_mac() def build_all_wrapper_libraries_windows(): python_compilers = get_available_python_compilers() if not len(python_compilers): print("[automate.py] ERROR: Visual Studio compiler not found") sys.exit(1) for msvs in python_compilers: vcvars = python_compilers[msvs] print("[automate.py] Build libcef_dll_wrapper libraries for" " VS{msvs}".format(msvs=msvs)) build_wrapper_library_windows(runtime_library=RUNTIME_MT, msvs=msvs, vcvars=vcvars) build_wrapper_library_windows(runtime_library=RUNTIME_MD, msvs=msvs, vcvars=vcvars) def build_wrapper_library_windows(runtime_library, msvs, vcvars): # When building library cmake variables file is being modified # for the /MD build. If the build fails and variables aren't # restored then the next /MT build would be broken. Make sure # that original contents of cmake variables files is always # restored. fix_cmake_variables_for_MD_library(try_undo=True) # Command to build libcef_dll_wrapper cmake_wrapper = prepare_build_command(build_lib=True, vcvars=vcvars) cmake_wrapper.extend(["cmake", "-G", "Ninja", "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) # Build directory and library path build_wrapper_dir = os.path.join( Options.cef_binary, "build_wrapper_{runtime_library}_VS{msvs}" .format(runtime_library=runtime_library, msvs=msvs)) wrapper_lib = os.path.join(build_wrapper_dir, "libcef_dll_wrapper", "libcef_dll_wrapper{ext}".format(ext=LIB_EXT)) # Check whether library is already built mt_already_built = False if os.path.exists(wrapper_lib): mt_already_built = True elif os.path.exists(build_wrapper_dir): # Last build failed, clean directory assert build_wrapper_dir shutil.rmtree(build_wrapper_dir) os.makedirs(build_wrapper_dir) else: os.makedirs(build_wrapper_dir) # Build library if mt_already_built: print("[automate.py] Already built: libcef_dll_wrapper" " /{runtime_library} for VS{msvs}" .format(runtime_library=runtime_library, msvs=msvs)) else: print("[automate.py] Build libcef_dll_wrapper" " /{runtime_library} for VS{msvs}" .format(runtime_library=runtime_library, msvs=msvs)) # Run cmake old_gyp_msvs_version = Options.gyp_msvs_version Options.gyp_msvs_version = msvs if runtime_library == RUNTIME_MD: fix_cmake_variables_for_MD_library() env = getenv() if msvs == "2010": # When Using WinSDK 7.1 vcvarsall.bat doesn't work. Use # setuptools.msvc.msvc9_query_vcvarsall to query env vars. env.update(msvc9_query_vcvarsall(10.0, arch=VS_PLATFORM_ARG)) # On Python 2.7 env values returned by both distutils # and setuptools are unicode, but Python expects env # dict values as strings. for env_key in env: env_value = env[env_key] if type(env_value) != str: env[env_key] = env_value.encode("utf-8") run_command(cmake_wrapper, working_dir=build_wrapper_dir, env=env) Options.gyp_msvs_version = old_gyp_msvs_version if runtime_library == RUNTIME_MD: fix_cmake_variables_for_MD_library(undo=True) print("[automate.py] cmake OK") # Run ninja ninja_wrapper = prepare_build_command(build_lib=True, vcvars=vcvars) ninja_wrapper.extend(["ninja", "libcef_dll_wrapper"]) run_command(ninja_wrapper, working_dir=build_wrapper_dir) print("[automate.py] ninja OK") assert os.path.exists(wrapper_lib) def fix_cmake_variables_permanently_windows(): """Changes to cef_variables.cmake are made permanently, there is no undo. """ # To get rid of these warnings: # > cl : Command line warning D9025 : overriding '/GR' with '/GR-' # > cl : Command line warning D9025 : overriding '/W3' with '/W4' # > cl : Command line warning D9025 : overriding '/MD' with '/MT' cmake_variables = os.path.join(Options.cef_binary, "cmake", "cef_variables.cmake") with open(cmake_variables, "rb") as fp: contents = fp.read().decode("utf-8") set1 = 'set(CMAKE_CXX_FLAGS_RELEASE "")' set2 = 'set(CMAKE_CXX_FLAGS "")' if "if(OS_WINDOWS)" not in contents: print("[automate.py] WARNING: failed to fix cmake variables" " permanently on Windows") return if set1 not in contents or set2 not in contents: contents = contents.replace("if(OS_WINDOWS)", "if(OS_WINDOWS)\n {set1}\n {set2}" .format(set1=set1, set2=set2)) else: return with open(cmake_variables, "wb") as fp: print("[automate.py] Fix permanently: {filename}" .format(filename=os.path.basename(cmake_variables))) fp.write(contents.encode("utf-8")) def fix_cmake_variables_for_MD_library(undo=False, try_undo=False): """Fix cmake variables or undo it. The try_undo param is for a case when want to be sure that the file wasn't modified, for example in case the last build failed.""" # Replace /MT with /MD /wd4275 in cef/cmake/cef_variables.cmake # Warnings are treated as errors so this needs to be ignored: # >> warning C4275: non dll-interface class 'stdext::exception' # >> used as base for dll-interface class 'std::bad_cast' # This warning occurs only in VS2008, in VS2013 not. # This replacements must be unique for the undo operation # to be reliable. mt_find = r"/MT " mt_replace = r"/MD /wd4275 " mtd_find = r"/MTd " mtd_replace = r"/MDd /wd4275 " cmake_variables = os.path.join(Options.cef_binary, "cmake", "cef_variables.cmake") with open(cmake_variables, "rb") as fp: contents = fp.read().decode("utf-8") if try_undo: matches1 = re.findall(re.escape(mt_replace), contents) matches2 = re.findall(re.escape(mtd_replace), contents) if len(matches1) or len(matches2): undo = True else: return if undo: (contents, count) = re.subn(re.escape(mt_replace), mt_find, contents) assert count == 2 (contents, count) = re.subn(re.escape(mtd_replace), mtd_find, contents) assert count == 1 else: (contents, count) = re.subn(re.escape(mt_find), mt_replace, contents) assert count == 2 (contents, count) = re.subn(re.escape(mtd_find), mtd_replace, contents) assert count == 1 with open(cmake_variables, "wb") as fp: fp.write(contents.encode("utf-8")) def build_wrapper_library_mac(): # On Mac it is required to link libcef_dll_wrapper against # libc++ library, so must build this library separately # from cefclient. cmake_wrapper = prepare_build_command(build_lib=True) cmake_wrapper.extend(["cmake", "-G", "Ninja", "-DPROJECT_ARCH=x86_64", "-DCMAKE_CXX_FLAGS=-stdlib=libc++", "-DCMAKE_BUILD_TYPE=" + Options.build_type, ".."]) build_wrapper_dir = os.path.join(Options.cef_binary, "build_wrapper") wrapper_lib = os.path.join(build_wrapper_dir, "libcef_dll_wrapper", "libcef_dll_wrapper.a") # Check whether already built already_built = False if os.path.exists(wrapper_lib): already_built = True elif os.path.exists(build_wrapper_dir): # Last build failed, clean directory assert build_wrapper_dir shutil.rmtree(build_wrapper_dir) os.makedirs(build_wrapper_dir) else: os.makedirs(build_wrapper_dir) # Build libcef_dll_wrapper library if already_built: print("[automate.py] Already built: libcef_dll_wrapper") else: print("[automate.py] Build libcef_dll_wrapper") # Cmake run_command(cmake_wrapper, build_wrapper_dir) print("[automate.py] cmake OK") # Ninja ninja_wrapper = prepare_build_command(build_lib=True) ninja_wrapper.extend(["ninja", "libcef_dll_wrapper"]) run_command(ninja_wrapper, build_wrapper_dir) print("[automate.py] ninja OK") assert os.path.exists(wrapper_lib) def prepare_build_command(build_lib=False, vcvars=None): """On Windows VS env variables must be set up by calling vcvarsall.bat""" command = list() if platform.system() == "Windows": if build_lib: if vcvars == VS2010_VCVARS: # When using WinSDK 7.1 vcvarsall.bat is broken. Instead # env variables are queried using setuptools.msvc. return command if vcvars: command.append(vcvars) else: command.append(get_vcvars_for_python()) command.append(VS_PLATFORM_ARG) else: if int(Options.cef_branch) >= 2704: command.append(VS2015_VCVARS) else: command.append(VS2013_VCVARS) command.append(VS_PLATFORM_ARG) command.append("&&") return command def fix_cef_include_files(): """Fixes to CEF include header files for eg. VS2008 on Windows.""" # TODO: This was fixed in upstream CEF, remove this code during # next CEF update on Windows. if platform.system() == "Windows" and get_msvs_for_python() == "2008": print("[automate.py] Fixing CEF include/ files") # cef_types_wrappers.h cef_types_wrappers = os.path.join(Options.cef_binary, "include", "internal", "cef_types_wrappers.h") with open(cef_types_wrappers, "rb") as fp: contents = fp.read().decode("utf-8") # error C2059: syntax error : '{' contents = contents.replace("s->range = {0, 0};", "s->range.from = 0; s->range.to = 0;") with open(cef_types_wrappers, "wb") as fp: fp.write(contents.encode("utf-8")) def create_prebuilt_binaries(): """After building copy binaries/libs to build/cef_xxxx/. Not all projects may have been built on all platforms.""" # Directories src = Options.cef_binary version_header = os.path.join(src, "include", "cef_version.h") dst = get_prebuilt_name(version_header) dst = os.path.join(Options.build_dir, dst) rmdir(dst) os.makedirs(dst) bindir = os.path.join(dst, "bin") libdir = os.path.join(dst, "lib") os.makedirs(bindir) os.makedirs(libdir) # Copy Release/Debug and Resources cpdir(os.path.join(src, Options.build_type), bindir) if not MAC: cpdir(os.path.join(src, "Resources"), bindir) # Fix id in CEF framework on Mac (currently it expects Frameworks/ dir) if MAC: new_id = ("@rpath/Chromium Embedded Framework.framework" "/Chromium Embedded Framework") cef_framework_dir = os.path.join( bindir, "Chromium Embedded Framework.framework") cef_library = os.path.join( cef_framework_dir, "Chromium Embedded Framework") assert os.path.isdir(cef_framework_dir) run_command(["install_name_tool", "-id", new_id, cef_library], working_dir=cef_framework_dir) # Copy cefclient, cefsimple, ceftests # cefclient cefclient = os.path.join( src, "build_cefclient", "tests", "cefclient", Options.build_type, "cefclient" + APP_EXT) if LINUX and os.path.exists(cefclient): # On Windows resources/*.html files are embedded inside exe cefclient_files = os.path.join( src, "build_cefclient", "tests", "cefclient", Options.build_type, "cefclient_files") cpdir(cefclient_files, os.path.join(bindir, "cefclient_files")) # cefsimple cefsimple = os.path.join( src, "build_cefclient", "tests", "cefsimple", Options.build_type, "cefsimple" + APP_EXT) # ceftests ceftests = os.path.join( src, "build_cefclient", "tests", "ceftests", Options.build_type, "ceftests" + APP_EXT) if LINUX and os.path.exists(ceftests): # On Windows resources/*.html files are embedded inside exe ceftests_files = os.path.join( src, "build_cefclient", "tests", "ceftests", Options.build_type, "ceftests_files") cpdir(ceftests_files, os.path.join(bindir, "ceftests_files")) def copy_app(app): if os.path.exists(app): if os.path.isdir(app): # On Mac app is a directory shutil.copytree(app, os.path.join(bindir, os.path.basename(app))) else: shutil.copy(app, bindir) if not MAC: # Currently do not copy apps on Mac copy_app(cefclient) copy_app(cefsimple) copy_app(ceftests) # END: Copy cefclient, cefsimple, ceftests # Copy libraries if platform.system() == "Windows": # libcef.lib and cef_sandbox.lib mvfiles(bindir, libdir, ".lib") python_compilers = get_available_python_compilers() for msvs in python_compilers: vs_subdir = os.path.join(libdir, "VS{msvs}".format(msvs=msvs)) os.makedirs(vs_subdir) # MT library libsrc = os.path.join( src, "build_wrapper_MT_VS{msvs}".format(msvs=msvs), "libcef_dll_wrapper", "libcef_dll_wrapper.lib") libdst = os.path.join(vs_subdir, "libcef_dll_wrapper_MT.lib") shutil.copy(libsrc, libdst) # MD library libsrc = os.path.join( src, "build_wrapper_MD_VS{msvs}".format(msvs=msvs), "libcef_dll_wrapper", "libcef_dll_wrapper.lib") libdst = os.path.join(vs_subdir, "libcef_dll_wrapper_MD.lib") shutil.copy(libsrc, libdst) elif platform.system() == "Darwin": shutil.copy(os.path.join(src, "build_wrapper", "libcef_dll_wrapper", "libcef_dll_wrapper.a"), libdir) else: # cefclient builds libcef_dll_wrapper by default and this version # is good for cefpython on Linux. On Windows and Mac # libcef_dll_wrapper is built seprately. shutil.copy(os.path.join(src, "build_cefclient", "libcef_dll_wrapper", "libcef_dll_wrapper.a"), libdir) # Remove .lib files from bin/ only after libraries were copied (Windows) libs = glob.glob(os.path.join(bindir, "*.lib")) for lib in libs: os.remove(lib) # Remove cef_sandbox.lib (huge file) cef_sandbox = os.path.join(libdir, "cef_sandbox.lib") if os.path.exists(cef_sandbox): os.remove(cef_sandbox) # Copy README.txt and LICENSE.txt shutil.copy(os.path.join(src, "README.txt"), dst) shutil.copy(os.path.join(src, "LICENSE.txt"), dst) print("[automate.py] OK prebuilt binaries created in '%s/'" % dst) def get_available_python_compilers(): all_python_compilers = OrderedDict([ ("2008", VS2008_VCVARS), ("2010", VS2010_VCVARS), ("2015", VS2015_VCVARS), ]) ret_compilers = OrderedDict() for msvs in all_python_compilers: vcvars = all_python_compilers[msvs] if os.path.exists(vcvars): ret_compilers[msvs] = vcvars else: print("[automate.py] INFO: Visual Studio compiler not found:" " {vcvars}".format(vcvars=vcvars)) return ret_compilers def get_vcvars_for_python(): msvs = get_msvs_for_python() return globals()["VS"+msvs+"_VCVARS"] def getenv(): """Env variables passed to shell when running commands. See cef/AutomatedBuildSetup.md for reference.""" env = os.environ # PATH if Options.build_cef: if os.path.exists(Options.depot_tools_dir): env["PATH"] = Options.depot_tools_dir + os.pathsep + env["PATH"] # Generators: ninja, msvs env["GYP_GENERATORS"] = Options.gyp_generators # VS version if platform.system() == "Windows": env["GYP_MSVS_VERSION"] = Options.gyp_msvs_version # GN configuration env["CEF_USE_GN"] = "1" # Issue #73 patch applied here with "use_allocator=none" env["GN_DEFINES"] = "use_sysroot=true use_allocator=none symbol_level=1" # env["GN_DEFINES"] += " use_gtk3=false" # To perform an official build set GYP_DEFINES=buildtype=Official. # This will disable debugging code and enable additional link-time # optimizations in Release builds. if Options.release_build and not Options.fast_build: env["GN_DEFINES"] += " is_official_build=true" # GYP configuration is DEPRECATED, however it is still set in # upstream Linux configuration on AutomatedBuildSetup wiki page, # so setting it here as well. env["GYP_DEFINES"] = "disable_nacl=1 use_sysroot=1 use_allocator=none" if Options.x86: env["GYP_DEFINES"] += " host_arch=x86_64 target_arch=ia32" if Options.release_build and not Options.fast_build: env["GYP_DEFINES"] += " buildtype=Official" # Modifications to upstream automate-git.py introduced # CEFPYTHON_NINJA_JOBS env key. env["CEFPYTHON_NINJA_JOBS"] = str(Options.ninja_jobs) return env def run_command(command, working_dir, env=None): """Run command in a given directory with env variables set. On Linux multiple commands on one line with the use of && are not allowed. """ if isinstance(command, list): command_str = " ".join(command) else: command_str = command print("[automate.py] Running '"+command_str+"' in '" + working_dir+"'...") if isinstance(command, str): args = shlex.split(command.replace("\\", "\\\\")) else: args = command if not env: env = getenv() # When passing list of args shell cannot be True on eg. Linux, read # notes in build.py shell = (platform.system() == "Windows") return subprocess.check_call(args, cwd=working_dir, env=env, shell=shell) def run_git(command_line, working_dir): """Run git command using depot_tools.""" return run_command("git %s" % command_line, working_dir) def run_automate_git(): """Run CEF automate-git.py using Python 2.7.""" script = os.path.join(Options.cefpython_dir, "tools", "automate-git.py") """ Example automate-git.py command: C:\chromium>call python automate-git.py --download-dir=./test/ --branch=2526 --no-debug-build --verbose-build Run ninja build manually: cd chromium/src ninja -v -j2 -Cout\Release cefclient """ args = [] if ARCH64 and not Options.x86: args.append("--x64-build") args.append("--download-dir=" + Options.cef_build_dir) args.append("--branch=" + Options.cef_branch) if Options.release_build: args.append("--no-debug-build") args.append("--verbose-build") # --force-build sets --force-distrib by default # ninja will only recompile files that changed args.append("--force-build") # We clone cef repository ourselves and update cef patches with ours, # so don't fetch/update CEF repo. args.append("--no-cef-update") # Force Chromium update so that gclient sync is called. It may fail # sometimes with files missing and must re-run to fix. if Options.force_chromium_update: args.append("--force-update") args.append("--no-distrib-archive") if platform.system() == "Linux": # Building cefclient target isn't supported on Linux when # using sysroot (cef/#1916). However building cefclient # later in cef_binary/ with cmake/ninja do works fine. args.append("--build-target=cefsimple") # On Windows automate-git.py must be run using Python 2.7 # from depot_tools. depot_tools should already be added to PATH. python = "python" # *do not* replace with sys.executable! args = " ".join(args) command = script + " " + args working_dir = Options.cef_build_dir return run_command("%s %s" % (python, command), working_dir) def rmdir(path): """Delete directory recursively.""" if os.path.exists(path): print("[automate.py] Removing directory %s" % path) shutil.rmtree(path, onerror=onerror) def cpdir(src, dst): """An equivalent of linux 'cp -r src/* dst/'. """ names = os.listdir(src) if not os.path.exists(dst): os.makedirs(dst) for name in names: path = os.path.join(src, name) if os.path.isdir(path): dst_subdir = os.path.join(dst, name) shutil.copytree(path, dst_subdir) else: shutil.copy(path, dst) def mvfiles(src, dst, ext): """An equivalent of linux 'mv src/*.ext dst/'. """ names = os.listdir(src) if not os.path.exists(dst): os.makedirs(dst) for name in names: path = os.path.join(src, name) if os.path.isfile(path) and name.endswith(ext): shutil.copy(path, dst) def onerror(func, path, _): """Fix file permission on error and retry operation.""" if not os.access(path, os.W_OK): os.chmod(path, stat.S_IWUSR) func(path) else: raise Exception("Not a file permission error, dunno what to do") def get_prebuilt_name(header_file=""): if header_file: version = get_version_from_file(header_file) else: version = get_cefpython_version() postfix2 = OS_POSTFIX2 if Options.x86: postfix2 = get_os_postfix2_for_arch("32bit") name = "cef%s_%s_%s" % ( version["CHROME_VERSION_MAJOR"], version["CEF_VERSION"], postfix2 ) return name if __name__ == "__main__": main()