diff --git a/api/ApplicationSettings.md b/api/ApplicationSettings.md index b0ec2d08..fba94f94 100644 --- a/api/ApplicationSettings.md +++ b/api/ApplicationSettings.md @@ -216,6 +216,8 @@ Custom flags that will be used when initializing the V8 Javascript engine. The consequences of using custom flags may not be well tested. Also configurable using the --js-flags switch. +To enable WebAssembly support set the `--expose-wasm` flag. + ### locale diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 1693e39f..b0c4a049 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -69,6 +69,9 @@ __Windows__ * Install an appropriate MS compiler for a specific Python version: https://wiki.python.org/moin/WindowsCompilers +* When using "Visual C++ compiler for Python 2.7" you have to install + "Microsoft Visual C++ 2008 Redistributable Package (x64)" from + [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) * To build CEF from sources: * Use Win7 x64 or later. 32-bit OS'es are not supported. For more details see [here](https://www.chromium.org/developers/how-tos/build-instructions-windows). diff --git a/examples/resources/back.gif b/examples/resources/back.gif new file mode 100644 index 00000000..92362f5e Binary files /dev/null and b/examples/resources/back.gif differ diff --git a/examples/resources/forward.gif b/examples/resources/forward.gif new file mode 100644 index 00000000..1032b9be Binary files /dev/null and b/examples/resources/forward.gif differ diff --git a/examples/resources/reload.gif b/examples/resources/reload.gif new file mode 100644 index 00000000..ef96a429 Binary files /dev/null and b/examples/resources/reload.gif differ diff --git a/examples/resources/tkinter.gif b/examples/resources/tkinter.gif new file mode 100644 index 00000000..cbd12220 Binary files /dev/null and b/examples/resources/tkinter.gif differ diff --git a/examples/resources/tkinter.png b/examples/resources/tkinter.png index 444ec8df..f593cb89 100644 Binary files a/examples/resources/tkinter.png and b/examples/resources/tkinter.png differ diff --git a/examples/tkinter_.py b/examples/tkinter_.py index 40acfd0f..c8385992 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -14,10 +14,13 @@ import Tkinter as tk import sys import os +import platform import logging as _logging # Globals logger = _logging.getLogger("tkinter_.py") +# Python 2.7 on Windows comes with Tk 8.5 which doesn't support PNG images +IMAGE_EXT = ".gif" if platform.system() == "Windows" else ".png" def main(): @@ -112,7 +115,7 @@ def get_browser_frame(self): def setup_icon(self): resources = os.path.join(os.path.dirname(__file__), "resources") - icon_path = os.path.join(resources, "tkinter.png") + icon_path = os.path.join(resources, "tkinter"+IMAGE_EXT) if os.path.exists(icon_path): self.icon = tk.PhotoImage(file=icon_path) # noinspection PyProtectedMember @@ -132,7 +135,7 @@ def __init__(self, master): resources = os.path.join(os.path.dirname(__file__), "resources") # Back button - back_png = os.path.join(resources, "back.png") + back_png = os.path.join(resources, "back"+IMAGE_EXT) if os.path.exists(back_png): self.back_image = tk.PhotoImage(file=back_png) self.back_button = tk.Button(self, image=self.back_image, @@ -140,7 +143,7 @@ def __init__(self, master): self.back_button.grid(row=0, column=0) # Forward button - forward_png = os.path.join(resources, "forward.png") + forward_png = os.path.join(resources, "forward"+IMAGE_EXT) if os.path.exists(forward_png): self.forward_image = tk.PhotoImage(file=forward_png) self.forward_button = tk.Button(self, image=self.forward_image, @@ -148,7 +151,7 @@ def __init__(self, master): self.forward_button.grid(row=0, column=1) # Reload button - reload_png = os.path.join(resources, "reload.png") + reload_png = os.path.join(resources, "reload"+IMAGE_EXT) if os.path.exists(reload_png): self.reload_image = tk.PhotoImage(file=reload_png) self.reload_button = tk.Button(self, image=self.reload_image, @@ -255,6 +258,7 @@ def embed_browser(self): window_info.SetAsChild(self.winfo_id()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") + assert self.browser self.browser.SetClientHandler(LoadHandler(self)) self.browser.SetClientHandler(FocusHandler(self)) self.message_loop_work() @@ -274,7 +278,11 @@ def on_root_configure(self): def on_mainframe_configure(self, width, height): if self.browser: - self.browser.SetBounds(0, 0, width, height) + if platform.system() == "Windows": + # noinspection PyUnresolvedReferences + cef.WindowUtils.OnSize(self.winfo_id(), 0, 0, 0) + elif platform.system() == "Linux": + self.browser.SetBounds(0, 0, width, height) self.browser.NotifyMoveOrResizeStarted() def on_focus_in(self, _): diff --git a/examples/wxpython.py b/examples/wxpython.py index d4a0a5e1..2de7a752 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -111,6 +111,7 @@ def OnSize(self, _): (width, height) = self.browser_panel.GetSizeTuple() # noinspection PyUnresolvedReferences self.browser.SetBounds(x, y, width, height) + self.browser.NotifyMoveOrResizeStarted() def OnClose(self, event): # In cefpython3.wx.chromectrl example calling browser.CloseBrowser() diff --git a/src/client_handler/client_handler_py27_win32.vcproj b/src/client_handler/client_handler_py27_win32.vcproj index 51670b21..07e4fbb1 100644 --- a/src/client_handler/client_handler_py27_win32.vcproj +++ b/src/client_handler/client_handler_py27_win32.vcproj @@ -4,7 +4,7 @@ Version="9.00" Name="client_handler_py27_win32" RootNamespace="client_handler_py27_win32" - ProjectGUID="{15AD928F-FFD0-4FA5-B469-E42ABB0B4196}" + ProjectGUID="{15AD928F-FFD0-4FA5-B469-E42AAA0B4196}" Keyword="Win32Proj" TargetFrameworkVersion="0" > @@ -27,7 +27,7 @@ browser, diff --git a/src/cpp_utils/cpp_utils_win32.vcproj b/src/cpp_utils/cpp_utils_win32.vcproj index cc2349da..e0f49c2f 100644 --- a/src/cpp_utils/cpp_utils_win32.vcproj +++ b/src/cpp_utils/cpp_utils_win32.vcproj @@ -27,6 +27,7 @@ @@ -27,7 +27,7 @@ + + + + + diff --git a/src/subprocess/main.cpp b/src/subprocess/main.cpp index fbf2d5bf..1192f4a8 100644 --- a/src/subprocess/main.cpp +++ b/src/subprocess/main.cpp @@ -8,22 +8,21 @@ #include int APIENTRY wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPTSTR lpCmdLine, - int nCmdShow) - + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); CefMainArgs mainArgs(hInstance); -#else // Mac, Linux +#else // defined(OS_WIN) int main(int argc, char **argv) { CefMainArgs mainArgs(argc, argv); -#endif +#endif // Mac, Linux CefRefPtr app(new CefPythonApp); int exitCode = CefExecuteProcess(mainArgs, app.get(), NULL); diff --git a/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp b/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp index 0de7ebf8..4ba8575b 100644 --- a/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp +++ b/src/subprocess/main_message_loop/main_message_loop_external_pump_win.cpp @@ -1,4 +1,5 @@ // Copied from upstream cefclient with minor modifications. +// Windows UNICODE API calls were converted to ANSI or commented out. // Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that @@ -50,7 +51,7 @@ MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin() : timer_pending_(false), main_thread_target_(NULL) { HINSTANCE hInstance = GetModuleHandle(NULL); - const wchar_t* const kClassName = L"CEFMainTargetHWND"; + const char* const kClassName = "CEFMainTargetHWND"; WNDCLASSEX wcex = {}; wcex.cbSize = sizeof(WNDCLASSEX); @@ -60,7 +61,7 @@ MainMessageLoopExternalPumpWin::MainMessageLoopExternalPumpWin() RegisterClassEx(&wcex); // Create the message handling window. - main_thread_target_ = CreateWindowW(kClassName, NULL, WS_OVERLAPPEDWINDOW, + main_thread_target_ = CreateWindowA(kClassName, NULL, WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, HWND_MESSAGE , NULL, hInstance, NULL); DCHECK(main_thread_target_); SetUserDataPtr(main_thread_target_, this); @@ -140,8 +141,8 @@ LRESULT CALLBACK MainMessageLoopExternalPumpWin::WndProc( } // namespace // static -scoped_ptr>MainMessageLoopExternalPump> +scoped_ptr MainMessageLoopExternalPump::Create() { - return scoped_ptr>MainMessageLoopExternalPump>( + return scoped_ptr( new MainMessageLoopExternalPumpWin()); } diff --git a/src/subprocess/main_message_loop/util_win.cpp b/src/subprocess/main_message_loop/util_win.cpp index 3dbb4c31..bc1f0965 100644 --- a/src/subprocess/main_message_loop/util_win.cpp +++ b/src/subprocess/main_message_loop/util_win.cpp @@ -1,4 +1,5 @@ // Copied from upstream cefclient with minor modifications. +// Windows UNICODE API calls were converted to ANSI or commented out. // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that @@ -25,12 +26,12 @@ WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc) { return old; } -std::wstring GetResourceString(UINT id) { - #define MAX_LOADSTRING 100 - TCHAR buff[MAX_LOADSTRING] = {0}; - LoadString(::GetModuleHandle(NULL), id, buff, MAX_LOADSTRING); - return buff; -} +//std::wstring GetResourceString(UINT id) { +// #define MAX_LOADSTRING 100 +// TCHAR buff[MAX_LOADSTRING] = {0}; +// LoadString(::GetModuleHandle(NULL), id, buff, MAX_LOADSTRING); +// return buff; +//} int GetCefMouseModifiers(WPARAM wparam) { int modifiers = 0; diff --git a/src/subprocess/main_message_loop/util_win.h b/src/subprocess/main_message_loop/util_win.h index 0f806ba5..39870204 100644 --- a/src/subprocess/main_message_loop/util_win.h +++ b/src/subprocess/main_message_loop/util_win.h @@ -1,4 +1,5 @@ // Copied from upstream cefclient with minor modifications. +// Windows UNICODE API calls were converted to ANSI or commented out. // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights // reserved. Use of this source code is governed by a BSD-style license that @@ -26,7 +27,7 @@ T GetUserDataPtr(HWND hWnd) { WNDPROC SetWndProcPtr(HWND hWnd, WNDPROC wndProc); // Return the resource string with the specified id. -std::wstring GetResourceString(UINT id); +//std::wstring GetResourceString(UINT id); int GetCefMouseModifiers(WPARAM wparam); int GetCefKeyboardModifiers(WPARAM wparam, LPARAM lparam); diff --git a/src/subprocess/subprocess_32bit.vcproj b/src/subprocess/subprocess_win32.vcproj similarity index 88% rename from src/subprocess/subprocess_32bit.vcproj rename to src/subprocess/subprocess_win32.vcproj index d5cf7d4a..18cff5e7 100644 --- a/src/subprocess/subprocess_32bit.vcproj +++ b/src/subprocess/subprocess_win32.vcproj @@ -31,8 +31,8 @@ Name="VCCLCompilerTool" Optimization="2" EnableIntrinsicFunctions="true" - AdditionalIncludeDirectories="../" - PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;RENDERER_PROCESS;" + AdditionalIncludeDirectories="$(INCLUDE);..\;..\common" + PreprocessorDefinitions="WIN32;_WIN32;_WINDOWS;WINVER=0x0601;_WIN32_WINNT=0x0601;UNICODE;_UNICODE;NOMINMAX;WIN32_LEAN_AND_MEAN;_HAS_EXCEPTIONS=0;NDEBUG;_NDEBUG;_CRT_SECURE_NO_WARNINGS;RENDERER_PROCESS;" ExceptionHandling="1" RuntimeLibrary="0" EnableFunctionLevelLinking="true" @@ -48,9 +48,10 @@ IgnoreImportLibrary="false" LinkLibraryDependencies="true" AdditionalDependencies="libcef.lib libcef_dll_wrapper_mt.lib" + AdditionalOptions="/MANIFEST:NO /LARGEADDRESSAWARE" LinkIncremental="1" - AdditionalLibraryDirectories="../../build/cef_win32/lib" - GenerateManifest="true" + AdditionalLibraryDirectories="$(AdditionalLibraryDirectories);$(LIB)" + GenerateManifest="false" IgnoreAllDefaultLibraries="false" GenerateDebugInformation="true" SubSystem="2" diff --git a/src/window_info.pyx b/src/window_info.pyx index c3bef322..988c665e 100644 --- a/src/window_info.pyx +++ b/src/window_info.pyx @@ -81,7 +81,16 @@ cdef class WindowInfo: cpdef py_void SetAsChild(self, WindowHandle parentWindowHandle, list windowRect=None): - if not WindowUtils.IsWindowHandle(parentWindowHandle): + # Allow parent window handle to be 0, in such case CEF will + # create top window automatically as in hello_world.py example. + IF UNAME_SYSNAME == "Windows": + # On Windows when parent window handle is 0 then SetAsPopup() + # must be called instead. + if parentWindowHandle == 0: + self.SetAsPopup(parentWindowHandle, "Popup") + return + if parentWindowHandle != 0\ + and not WindowUtils.IsWindowHandle(parentWindowHandle): raise Exception("Invalid parentWindowHandle: %s"\ % parentWindowHandle) self.windowType = "child" @@ -100,7 +109,10 @@ cdef class WindowInfo: IF UNAME_SYSNAME == "Windows": cpdef py_void SetAsPopup(self, WindowHandle parentWindowHandle, py_string windowName): - if not WindowUtils.IsWindowHandle(parentWindowHandle): + # Allow parent window handle to be 0, in such case CEF will + # create top window automatically as in hello_world.py example. + if parentWindowHandle != 0\ + and not WindowUtils.IsWindowHandle(parentWindowHandle): raise Exception("Invalid parentWindowHandle: %s"\ % parentWindowHandle) self.parentWindowHandle = parentWindowHandle @@ -109,7 +121,7 @@ cdef class WindowInfo: cpdef py_void SetAsOffscreen(self, WindowHandle parentWindowHandle): - # It is allowed to pass 0 as parentWindowHandle. + # It is allowed to pass 0 as parentWindowHandle in OSR mode if parentWindowHandle and \ not WindowUtils.IsWindowHandle(parentWindowHandle): raise Exception("Invalid parentWindowHandle: %s" \ diff --git a/src/windows/compile.bat b/src/windows/compile.bat deleted file mode 100644 index eacaee71..00000000 --- a/src/windows/compile.bat +++ /dev/null @@ -1,235 +0,0 @@ -@echo off - -:: It's best to always call with a flag that specifies python -:: version and architecture (eg. --py27-32bit). This will ensure -:: that PATH contains only minimum set of directories and will -:: allow to detect possible issues early. - -:: Arguments -if [%1] == [] ( - echo [compile.bat] Version number not provided. Usage: compile.bat 31.0 - echo [compile.bat] Opt: --rebuild --py27-32bit --py27-64bit --py34-32bit - echo --py34-64bit - exit /B 1 -) - -:: --rebuild flag to rebuild all vcproj builds -set rebuild_flag=0 -echo.%*|findstr /C:"--rebuild" >nul 2>&1 -if %errorlevel% equ 0 ( - set rebuild_flag=1 -) - -:: Add only Python/ to PATH. -:: --py27-32bit flag -echo.%*|findstr /C:"--py27-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27 -) -:: --py27-64bit flag -echo.%*|findstr /C:"--py27-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27_x64;C:\Python27_amd64;C:\Python27_64 -) -:: --py34-32bit flag -echo.%*|findstr /C:"--py34-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34 -) -:: --py34-64bit flag -echo.%*|findstr /C:"--py34-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34_x64;C:\Python34_amd64;C:\Python34_64 -) -:: PATH -echo [compile.bat] PATH: %PATH% - -:: Version number -set version=%1 -echo [compile.bat] Version argument: %version% - -:: Python architecture. %bits%=="32bit" or "64bit" -FOR /F "delims=" %%i IN ('python -c "import struct, sys; sys.stdout.write(str(8 * struct.calcsize('P')) + 'bit');"') do set bits=%%i -echo [compile.bat] Python architecture: %bits% - -:: Cython version -FOR /F "delims=" %%i IN ('python -c "import sys, Cython; sys.stdout.write(Cython.__version__);"') do set cython_version=%%i -echo [compile.bat] Cython version: %cython_version% - -:: Python version -for /F %%i in ('python -c "import sys; sys.stdout.write(str(sys.version_info[0])+str(sys.version_info[1]));"') do set pyver=%%i -echo [compile.bat] Python version: py%pyver% - -:: Binaries directory -set binaries=%~dp0binaries_%bits% -echo [compile.bat] Binaries directory: %binaries% - -:: Setup directory -set setup=%~dp0setup -echo [compile.bat] Setup directory: %setup% - -:: Delete .pyd files -echo [compile.bat] Cleaning cython build files from previous run -del "%binaries%\cefpython_py%pyver%.pyd" -del "%setup%\cefpython_py%pyver%.pyd" -for /R %setup% %%f in (*.pyx) do del "%%f" -rmdir /S /Q "%setup%\build\" - -:: Fix cefpython.h -echo [compile.bat] Fixing cefpython.h -cd %setup% -python fix_cefpython_h.py -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: failed to fix cefpython.h - cd ../ - exit /B 1 -) -cd ../ - -:: Compile VS projects: client_handler, libcefpythonapp, subprocess, cpp_utils - -:: client_handler paths -set client_handler_dir=%~dp0..\client_handler -set client_handler_vcproj=%client_handler_dir%\client_handler_py%pyver%_%bits%.vcproj - -set subprocess_dir=%~dp0..\subprocess - -:: libcefpythonapp paths -set libcefpythonapp_vcproj=%subprocess_dir%\libcefpythonapp_py%pyver%_%bits%.vcproj - -:: subprocess paths -set subprocess_vcproj=%subprocess_dir%\subprocess_%bits%.vcproj - -:: cpp_utils paths -set cpp_utils_dir=%~dp0..\..\cpp_utils -set cpp_utils_vcproj=%cpp_utils_dir%\cpp_utils_%bits%.vcproj - -set success=0 -if "%pyver%"=="27" ( - if "%bits%"=="32bit" ( - set "vcbuild=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" - set success=1 - ) - if "%bits%"=="64bit" ( - REM :: The same vcbuild.exe 32-bit for building x64 - set "vcbuild=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcpackages\vcbuild.exe" - set success=1 - ) - set "vcoptions=/nocolor /nologo /nohtmllog" - if %rebuild_flag% equ 1 ( - set "vcoptions=%vcoptions% /rebuild" - ) -) -if "%pyver%"=="34" ( - :: In VS2010 vcbuild was replaced by msbuild.exe. - :: /clp:disableconsolecolor - :: msbuild /p:BuildProjectReferences=false project.proj - :: MSBuild.exe MyProject.proj /t:build -) - -if %success% neq 1 ( - echo [compile.bat] ERROR: failed determining tool to build vcproj files - exit /B 1 -) - -echo [compile.bat] Building client_handler vcproj -"%vcbuild%" %vcoptions% %client_handler_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building client_handler vcproj failed - exit /B 1 -) - -echo [compile.bat] Building libcefpythonapp vcproj -"%vcbuild%" %vcoptions% %libcefpythonapp_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building libcefpythonapp vcproj failed - exit /B 1 -) - -echo [compile.bat] Building subprocess vcproj -"%vcbuild%" %vcoptions% %subprocess_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building subprocess vcproj failed - exit /B 1 -) - -echo [compile.bat] Building cpp_utils vcproj -"%vcbuild%" %vcoptions% %cpp_utils_vcproj% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: building cpp_utils vcproj failed - exit /B 1 -) - -:: Do not clean VS build files, as this would slow down the process -:: of recompiling. - -:: Compile .rc file to a .res object. -echo [compile.bat] Compiling cefpython.rc file to a .res object -cd %setup%\ -python compile_rc.py -v %version% -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: compiling .rc file failed - exit /B 1 -) - -echo [compile.bat] Entering setup/ directory -cd %setup% - -echo [compile.bat] Copying .pyx files to setup/ directory and fixing includes -python fix_pyx_files.py -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: running fix_pyx_files.py failed - exit /B 1 -) - -:: __version__.pyx must be generated after running fix_pyx_files.py, -:: as that script deletes old pyx files before copying new ones. -echo [compile.bat] Creating __version__.pyx file -echo __version__ = "%version%">>__version__.pyx -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: writing __version__.pyx failed - exit /B 1 -) - -echo [compile.bat] Running the cython setup.py script -python setup.py build_ext --inplace -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: the cython setup.py script failed - :: Clean files from the build that failed - for /R %setup% %%f in (*.pyx) do del "%%f" - for /R %setup% %%f in (*.res) do del "%%f" - rmdir /S /Q "%setup%\build\" - cd ../ - exit /B 1 -) - -echo [compile.bat] Fixing cefpython.h -python fix_cefpython_h.py -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: failed to fix cefpython.h - exit /B 1 -) - -echo [compile.bat] Cleaning files from the build -for /R %setup% %%f in (*.pyx) do del "%%f" -for /R %setup% %%f in (*.res) do del "%%f" -rmdir /S /Q "%setup%\build\" - -echo [compile.bat] Moving the pyd module to the binaries directory -move "%setup%\cefpython_py%pyver%.pyd" "%binaries%/cefpython_py%pyver%.pyd" -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: Moving the pyd module failed - exit /B 1 -) - -echo [compile.bat] Copying subprocess.exe to the binaries directory -copy "%~dp0..\subprocess\Release_%bits%\subprocess_%bits%.exe" "%binaries%\subprocess.exe" -if %errorlevel% neq 0 ( - echo [compile.bat] ERROR: Copying subprocess.exe failed - exit /B 1 -) - -echo [compile.bat] Everything went OK. Running the wxpython.py example.. - -cd %binaries% -python wxpython.py & cd ../ diff --git a/src/windows/installer/.gitignore b/src/windows/installer/.gitignore deleted file mode 100644 index 0dc5ef2a..00000000 --- a/src/windows/installer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -cefpython3-*/ -Output/ -dist/ diff --git a/src/windows/installer/README.txt b/src/windows/installer/README.txt deleted file mode 100644 index 2325940f..00000000 --- a/src/windows/installer/README.txt +++ /dev/null @@ -1,18 +0,0 @@ -1. To install CEF Python 3 type: - - python setup.py install - -2. In the same directory that setup.py resides there is - an examples/ directory, run some example scripts from there: - - cd examples/ - python wxpython.py - python pyqt.py - python pyside.py - python pygtk_.py - python pywin32.py - - cd wx/ - python sample1.py - python sample2.py - python sample3.py diff --git a/src/windows/installer/__init__.py.template b/src/windows/installer/__init__.py.template deleted file mode 100644 index 94ef341b..00000000 --- a/src/windows/installer/__init__.py.template +++ /dev/null @@ -1,12 +0,0 @@ -__all__ = ["cefpython", "wx"] -__version__ = "%(APP_VERSION)s" -__author__ = "The CEF Python authors" - -import sys - -if 0x02070000 <= sys.hexversion < 0x03000000: - from . import cefpython_py27 as cefpython -elif 0x03000000 <= sys.hexversion < 0x04000000: - from . import cefpython_py32 as cefpython -else: - raise Exception("Unsupported python version: " + sys.version) diff --git a/src/windows/installer/build_all.bat b/src/windows/installer/build_all.bat deleted file mode 100644 index 3c2dfdea..00000000 --- a/src/windows/installer/build_all.bat +++ /dev/null @@ -1,213 +0,0 @@ -@echo off -setlocal ENABLEDELAYEDEXPANSION - -:: It's best to always call with a flag that specifies python -:: version and architecture (eg. --py27-32bit). This will ensure -:: that PATH contains only minimum set of directories and will -:: allow to detect possible issues early. - -if "%1"=="" goto usage -if "%2"=="" goto usage - -set version=%1 - -:: Add only Python/ and Python/Scripts/ to PATH. -:: --py27-32bit flag -echo.%*|findstr /C:"--py27-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27;C:\Python27\Scripts -) -:: --py27-64bit flag -echo.%*|findstr /C:"--py27-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python27_x64;C:\Python27_amd64;C:\Python27_64;C:\Python27_x64\Scripts;C:\Python27_amd64\Scripts;C:\Python27_64\Scripts -) -:: --py34-32bit flag -echo.%*|findstr /C:"--py34-32bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34;C:\Python34\Scripts -) -:: --py34-64bit flag -echo.%*|findstr /C:"--py34-64bit" >nul 2>&1 -if %errorlevel% equ 0 ( - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Python34_x64;C:\Python34_amd64;C:\Python34_64;C:\Python34_x64\Scripts;C:\Python34_amd64\Scripts;C:\Python34_64\Scripts -) -:: PATH -echo [compile.bat] PATH: %PATH% - -:: Python architecture. %bits%=="32bit" or "64bit" -FOR /F "delims=" %%i IN ('python -c "import struct, sys; sys.stdout.write(str(8 * struct.calcsize('P')) + 'bit');"') do set bits=%%i -echo [compile.bat] Python architecture: %bits% -set success=0 -if "%bits%"=="32bit" ( - set platform=win32 - set success=1 -) -if "%bits%"=="64bit" ( - set platform=win-amd64 - set success=1 -) -if %success% neq 1 ( - echo [build_all.bat] ERROR: invalid architecture: %bits% - exit /B 1 -) - -echo [build_all.bat] PLATFORM: %platform% -echo [build_all.bat] VERSION: %version% - -:: Python version -for /F %%i in ('python -c "import sys; sys.stdout.write(str(sys.version_info[0]) + '.' + str(sys.version_info[1]));"') do set pyverdot=%%i -echo [build_all.bat] Python version: py%pyverdot% - -:: --disable-inno-setup flag -set DISABLE_INNO_SETUP=0 -echo.%*|findstr /C:"--disable-inno-setup" >nul 2>&1 -if %errorlevel% equ 0 ( - set DISABLE_INNO_SETUP=1 -) - -:: Clean directories from previous run -rmdir /s /q Output -for /f "tokens=*" %%f in ('dir .\cefpython3*setup /ad/b') do rmdir /s /q %%f -rmdir /s /q dist - -mkdir dist - -echo [build_all.bat] Installing setuptools and wheel -pip install setuptools wheel -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: pip install setuptools wheel - exit /B 1 -) - -if %DISABLE_INNO_SETUP% equ 0 ( - echo [build_all.bat] Creating Inno Setup intaller - python make-installer.py -v %version% - if !errorlevel! equ 0 ( - for /f "tokens=*" %%f in ('dir .\Output\*.exe /b') do ( - move .\Output\%%f dist/%%f - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: moving inno setup installer failed - exit /B 1 - ) - ) - rmdir Output - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: deleting Output/ directory failed - exit /B 1 - ) - ) - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: creating Inno Setup installer failed - exit /B 1 - ) -) - -echo [build_all.bat] Creating Distutils setup -python make-setup.py -v %version% -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Distutils setup - exit /B 1 -) - -:: Enter the setup directory -for /f "tokens=*" %%f in ('dir .\cefpython3*setup /ad/b') do cd %%f - -echo [build_all.bat] Creating Distutils source package -python setup.py sdist -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Distutils source package - exit /B 1 -) - -echo [build_all.bat] Creating Python Egg -python setup.py bdist_egg -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Python Egg failed - exit /B 1 -) - -echo [build_all.bat] Creating Python Wheel -python setup.py bdist_wheel -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating Python Wheel failed - exit /B 1 -) - -echo [build_all.bat] Creating MSI installer -python setup.py bdist_msi -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating MSI installer failed - exit /B 1 -) - -echo [build_all.bat] Creating EXE installer -python setup.py bdist_wininst -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: creating EXE installer failed - exit /B 1 -) - -echo [build_all.bat] Moving all packages to the dist/ directory -set success=0 -for /f "tokens=*" %%f in ('dir .\dist\*.* /b') do ( - move .\dist\%%f .\..\dist\%%f - if !errorlevel! neq 0 ( - echo [build_all.bat] ERROR: moving setup dist/ packages failed - exit /B 1 - ) - if !errorlevel! equ 0 ( - set success=1 - ) -) -if %success% neq 1 ( - echo [build_all.bat] ERROR: moving setup dist/ packages failed - exit /B 1 -) - -:: Up to the installer/ directory -cd ../ - -echo [build_all.bat] Deleting the Distutils setup directory -for /f "tokens=*" %%f in ('dir .\cefpython3*setup /ad/b') do rmdir /s /q %%f -if %errorlevel% neq 0 ( - echo [build_all.bat] ERROR: failed deleting the Distutils setup directory - exit /B 1 -) - -cd dist/ - -echo [build_all.bat] Renaming some of the packages to include platform tag -for /R %%i in (*) do ( - set oldfile=%%i - set newfile=!oldfile:.egg=-%platform%.egg! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) - set oldfile=%%i - set newfile=!oldfile:.zip=-py%pyverdot%-%platform%.zip! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) - set oldfile=%%i - set newfile=!oldfile:%platform%.exe=py%pyverdot%-%platform%.exe! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) - set oldfile=%%i - set newfile=!oldfile:%platform%.msi=py%pyverdot%-%platform%.msi! - if "!oldfile!" neq "!newfile!" ( - move !oldfile! !newfile! - ) -) - -echo [build_all.bat] Packages in the dist/ directory: -dir - -echo OK - -goto :eof -:usage -@echo [build_all.bat] ERROR: platform or version arguments missing or invalid -@echo [build_all.bat] ERROR: example usage: build_all.bat win32 31.2 -exit /B 1 diff --git a/src/windows/installer/innosetup.template b/src/windows/installer/innosetup.template deleted file mode 100644 index e3a9a51e..00000000 --- a/src/windows/installer/innosetup.template +++ /dev/null @@ -1,165 +0,0 @@ -; Parts of this code was taken from wxPython/distrib/make_installer.py - -[Setup] - -AppName = CEF Python 3 for Python %(PYTHON_VERSION)s %(APP_NAME_BITS)s -AppVersion = %(APP_VERSION)s -AppVerName = CEF Python 3 version %(APP_VERSION)s for Python %(PYTHON_VERSION)s %(PYTHON_ARCHITECTURE)s - -AppPublisher = Czarek Tomczak -AppPublisherURL = http://code.google.com/cefpython/ -AppSupportURL = https://groups.google.com/group/cefpython?hl=en -AppUpdatesURL = http://code.google.com/cefpython/ -AppCopyright = Copyright 2012-2013 Czarek Tomczak - -DefaultDirName = {code:GetInstallDir|c:\Python} - -DefaultGroupName = CEF Python 3 for Python %(PYTHON_VERSION)s %(APP_NAME_BITS)s -PrivilegesRequired = none -DisableStartupPrompt = yes -Compression = zip -DirExistsWarning = no -DisableReadyMemo = yes -DisableReadyPage = yes -DisableDirPage = no -DisableProgramGroupPage = no -UsePreviousAppDir = yes -UsePreviousGroup = yes - -SourceDir = %(BINARIES_DIR)s -OutputDir = %(INSTALLER_DIR)s\Output -OutputBaseFilename = %(PACKAGE_NAME)s-%(APP_VERSION)s.%(PLATFORM)s-py%(PYTHON_VERSION)s-innosetup - -UninstallFilesDir = {app}\%(PACKAGE_NAME)s -LicenseFile = %(BINARIES_DIR)s\LICENSE.txt - -[Icons] - -Name: "{group}\Examples"; Filename: "{app}\%(PACKAGE_NAME)s\examples"; -Name: "{group}\Uninstall Package"; Filename: "{uninstallexe}"; - -[Run] - -Filename: "{app}\%(PACKAGE_NAME)s\examples"; Flags: postinstall skipifsilent shellexec; - -[Files] - -Source: "*.dll"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "*.pak"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "locales\*.pak"; DestDir: "{app}\%(PACKAGE_NAME)s\locales"; Flags: ignoreversion; -Source: "%(INSTALLER_DIR)s\__init__.py.generated"; DestDir: "{app}\%(PACKAGE_NAME)s"; DestName: "__init__.py"; Flags: ignoreversion; -Source: "cefclient.exe"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "cefpython_py%(PYTHON_VERSION_NODOT)s.pyd"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "LICENSE.txt"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "README.txt"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; -Source: "subprocess.exe"; DestDir: "{app}\%(PACKAGE_NAME)s"; Flags: ignoreversion; - -; ------------------------------------------------------------------------------ -; wx subpackage -; ------------------------------------------------------------------------------ - -Source: "%(WX_SUBPACKAGE_DIR)s\*.py"; DestDir: "{app}\%(PACKAGE_NAME)s\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\*.txt"; DestDir: "{app}\%(PACKAGE_NAME)s\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\images\*.png"; DestDir: "{app}\%(PACKAGE_NAME)s\wx\images"; Flags: ignoreversion; - -; ------------------------------------------------------------------------------ -; wx examples -; ------------------------------------------------------------------------------ - -Source: "%(WX_SUBPACKAGE_DIR)s\examples\*.py"; DestDir: "{app}\%(PACKAGE_NAME)s\examples\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\examples\*.html"; DestDir: "{app}\%(PACKAGE_NAME)s\examples\wx"; Flags: ignoreversion; -Source: "%(WX_SUBPACKAGE_DIR)s\examples\*.png"; DestDir: "{app}\%(PACKAGE_NAME)s\examples\wx"; Flags: ignoreversion; - -; ------------------------------------------------------------------------------ -; examples -; ------------------------------------------------------------------------------ - -Source: "*.py"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.html"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.css"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.js"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; -Source: "*.ico"; DestDir: "{app}\%(PACKAGE_NAME)s\examples"; Flags: ignoreversion; - -[UninstallDelete] - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\__pycache__" - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\examples\__pycache__" - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\wx\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\examples\wx\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\examples\wx\__pycache__" - -Type: files; Name: "{app}\%(PACKAGE_NAME)s\wx\*.pyc"; -Type: files; Name: "{app}\%(PACKAGE_NAME)s\wx\*.log"; -Type: filesandordirs; Name: "{app}\%(PACKAGE_NAME)s\wx\__pycache__" - -[Code] - -program Setup; -var - PythonDir : String; - InstallDir : String; - -function InitializeSetup(): Boolean; -begin - - if not RegQueryStringValue(%(HKEY_CURRENT_USER)s, - 'Software\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - if not RegQueryStringValue(%(HKEY_LOCAL_MACHINE)s, - 'Software\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - if not RegQueryStringValue(%(HKEY_CURRENT_USER)s, - 'Software\Wow6432Node\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - if not RegQueryStringValue(%(HKEY_LOCAL_MACHINE)s, - 'Software\Wow6432Node\Python\PythonCore\%(PYTHON_VERSION)s\InstallPath', - '', PythonDir) then begin - - MsgBox('No installation of Python %(PYTHON_VERSION)s ' - + 'found in registry.' + #13 + 'Be sure to enter ' - + 'a pathname that places Python on the ' - + 'PYTHONPATH', - mbConfirmation, MB_OK); - PythonDir := 'C:\Python'; - end; - end; - end; - end; - - InstallDir := PythonDir + '\Lib\site-packages'; - Result := True; -end; - -function GetInstallDir(Default: String): String; -begin - Result := InstallDir; -end; - -function UninstallOld(FileName: String): Boolean; -var - ResultCode: Integer; -begin - Result := False; - if FileExists(FileName) then begin - Result := True; - Exec(FileName, '/SILENT', WizardDirValue(), SW_SHOWNORMAL, - ewWaitUntilTerminated, ResultCode); - end; -end; - -function NextButtonClick(CurPage: Integer): Boolean; -begin - Result := True; - if CurPage <> wpSelectDir then Exit; - UninstallOld(WizardDirValue() + '\%(PACKAGE_NAME)s\unins001.exe') - UninstallOld(WizardDirValue() + '\%(PACKAGE_NAME)s\unins000.exe') -end; diff --git a/src/windows/installer/make-installer.py b/src/windows/installer/make-installer.py deleted file mode 100644 index ab31d536..00000000 --- a/src/windows/installer/make-installer.py +++ /dev/null @@ -1,93 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# Create a Windows package installer. - -import sys -import os -import platform -import argparse -import re -import struct -import sysconfig - -BITS = str(8 * struct.calcsize('P')) + 'bit' -assert (BITS == "32bit" or BITS == "64bit") - -ISCC = r"c:\Program Files (x86)\Inno Setup 5\ISCC.exe" -if "INNO5" in os.environ: - ISCC = os.environ["INNO5"] - -TEMPLATE_FILE = os.getcwd()+r"\innosetup.template" -ISS_FILE = os.getcwd()+r"\innosetup.generated" - -def main(): - parser = argparse.ArgumentParser(usage="%(prog)s [options]") - parser.add_argument("-v", "--version", help="cefpython version", - required=True) - args = parser.parse_args() - assert re.search(r"^\d+\.\d+$", args.version), "Invalid version string" - - vars = {} - vars["PACKAGE_NAME"] = "cefpython3" - vars["APP_VERSION"] = args.version - vars["PYTHON_VERSION"] = (str(sys.version_info.major) + "." - + str(sys.version_info.minor)) - vars["PYTHON_VERSION_NODOT"] = (str(sys.version_info.major) + "" - + str(sys.version_info.minor)) - vars["PYTHON_ARCHITECTURE"] = platform.architecture()[0] - vars["BINARIES_DIR"] = os.path.realpath( - os.getcwd() + r"\..\binaries_%s" % BITS) - vars["PYD_FILE"] = (vars["BINARIES_DIR"]+r"\cefpython_py" - + str(sys.version_info.major) + str(sys.version_info.minor) - + ".pyd") - vars["INSTALLER_DIR"] = os.getcwd() - vars["WX_SUBPACKAGE_DIR"] = os.path.realpath(os.getcwd()+r"\..\..\wx") - vars["PLATFORM"] = sysconfig.get_platform() - - if BITS == "32bit": - # We must keep compatibility, 32bit installers didn't contain - # architecture information in AppName. So make it an empty string. - vars["APP_NAME_BITS"] = "" - vars["HKEY_CURRENT_USER"] = "HKEY_CURRENT_USER" - vars["HKEY_LOCAL_MACHINE"] = "HKEY_LOCAL_MACHINE" - elif BITS == "64bit": - vars["APP_NAME_BITS"] = "64bit" - # Inno setup installer is a 32bit application. To query 64bit - # registry from within 32bit application you need to add _64 - # postfix. - vars["HKEY_CURRENT_USER"] = "HKEY_CURRENT_USER_64" - vars["HKEY_LOCAL_MACHINE"] = "HKEY_LOCAL_MACHINE_64" - - print("Reading template: %s" % TEMPLATE_FILE) - - f = open(TEMPLATE_FILE) - template = f.read() - f.close() - - f = open(ISS_FILE, "w") - f.write(template % vars) - f.close() - - print("Saved: %s" % ISS_FILE) - - initPyTemplate = os.getcwd()+r"\__init__.py.template" - initPyInstall = os.getcwd()+r"\__init__.py.generated" - - f = open(initPyTemplate) - initPyTemplateCode = f.read() - f.close() - - f = open(initPyInstall, "w") - f.write(initPyTemplateCode % vars) - f.close() - print("Saved: %s" % initPyInstall) - - iscc_command = '"'+ ISCC + '" ' + ISS_FILE - print("Running ISCC: %s" % iscc_command) - exit_code = os.system(iscc_command) - sys.exit(exit_code) - -if __name__ == "__main__": - main() diff --git a/src/windows/installer/make-setup.py b/src/windows/installer/make-setup.py deleted file mode 100644 index 27d521e3..00000000 --- a/src/windows/installer/make-setup.py +++ /dev/null @@ -1,178 +0,0 @@ -# Copyright (c) 2012-2014 The CEF Python authors. All rights reserved. -# License: New BSD License. -# Website: http://code.google.com/p/cefpython/ - -# Create a setup package. - -import sys -import os -import platform -import argparse -import re -import platform -import shutil -import glob -import shutil -import sysconfig - -BITS = platform.architecture()[0] -assert (BITS == "32bit" or BITS == "64bit") - -PACKAGE_NAME = "cefpython3" - -README_FILE = os.getcwd()+r"/README.txt" -INIT_TEMPLATE = os.getcwd()+r"/__init__.py.template" -SETUP_TEMPLATE = os.getcwd()+r"/setup.py.template" -SETUP_CFG_TEMPLATE = os.getcwd()+r"/setup.cfg.template" - -def glob_remove(pathname): - filelist = glob.glob(pathname) - for f in filelist: - os.remove(f) - -def glob_copy(src_glob, dst_folder): - for fname in glob.iglob(src_glob): - print("Copying %s to %s" % (fname, dst_folder)) - if os.path.isdir(fname): - shutil.copytree(fname, - os.path.join(dst_folder, os.path.basename(fname))) - else: - shutil.copy(fname, - os.path.join(dst_folder, os.path.basename(fname))) - -def glob_move(src_glob, dst_folder): - if not os.path.exists(dst_folder): - os.mkdir(dst_folder) - for fname in glob.iglob(src_glob): - shutil.move(fname, - os.path.join(dst_folder, os.path.basename(fname))) - -def str_format(string, dictionary): - orig_string = string - for key, value in dictionary.iteritems(): - string = string.replace("%("+key+")s", value) - if string == orig_string: - raise Exception("Nothing to format") - if re.search(r"%\([a-zA-Z0-9_]+\)s", string): - raise Exception("Not all strings formatted") - return string - -def main(): - parser = argparse.ArgumentParser(usage="%(prog)s [options]") - parser.add_argument("-v", "--version", help="cefpython version", - required=True) - args = parser.parse_args() - assert re.search(r"^\d+\.\d+$", args.version), ( - "Invalid version string") - - vars = {} - vars["APP_VERSION"] = args.version - vars["PLATFORM"] = sysconfig.get_platform() - vars["PY_VERSION_DIGITS_ONLY"] = (str(sys.version_info.major) + "" - + str(sys.version_info.minor)) # "27" or "34" - - print("Reading template: %s" % README_FILE) - f = open(README_FILE) - README_CONTENT = f.read() - f.close() - - print("Reading template: %s" % INIT_TEMPLATE) - f = open(INIT_TEMPLATE) - INIT_CONTENT = str_format(f.read(), vars) - f.close() - - print("Reading template: %s" % SETUP_TEMPLATE) - f = open(SETUP_TEMPLATE) - SETUP_CONTENT = str_format(f.read(), vars) - f.close() - - print("Reading template: %s" % SETUP_CFG_TEMPLATE) - f = open(SETUP_CFG_TEMPLATE) - SETUP_CFG_CONTENT = str_format(f.read(), vars) - f.close() - - installer_dir = os.path.dirname(os.path.abspath(__file__)) - - pyVersion = str(sys.version_info.major) +"."+ str(sys.version_info.minor) - setup_dir = installer_dir+"/"+PACKAGE_NAME+"-"+vars["APP_VERSION"]\ - +"."+BITS+"-py"+pyVersion+"-setup" - print("Creating setup dir: "+setup_dir) - os.mkdir(setup_dir) - - package_dir = setup_dir+"/"+PACKAGE_NAME - #print("Creating package dir") - #os.mkdir(package_dir) - - print("Creating README.txt from template") - with open(setup_dir+"/README.txt", "w") as f: - f.write(README_CONTENT) - - print("Creating setup.py from template") - with open(setup_dir+"/setup.py", "w") as f: - f.write(SETUP_CONTENT) - - print("Creating setup.cfg from template") - with open(setup_dir+"/setup.cfg", "w") as f: - f.write(SETUP_CFG_CONTENT) - - binaries_dir = os.path.abspath(installer_dir+"/../binaries_"+BITS+"/") - print("Copying binaries to package dir") - shutil.copytree(binaries_dir, package_dir) - - os.chdir(package_dir) - print("Removing .log .pyc .pdb files from the package dir") - glob_remove("*.log") - glob_remove("*.pyc") - glob_remove("*.pdb") - - os.chdir(installer_dir) - - print("Creating __init__.py from template") - with open(package_dir+"/__init__.py", "w") as f: - f.write(INIT_CONTENT) - - print("Creating examples dir in package dir") - os.mkdir(package_dir+"/examples/") - - print("Creating wx dir in package dir") - os.mkdir(package_dir+"/wx/") - - print("Moving example scripts from package dir to examples dir") - examples = glob.glob(package_dir+"/*.py") - for example in examples: - # Ignore: cefpython_py27.py - dummy API script - if os.path.basename(example).startswith("cefpython_"): - continue - # Ignore: __init__.py - if os.path.basename(example).startswith("__"): - continue - os.rename(example, package_dir+"/examples/"+os.path.basename(example)) - glob_move(package_dir+"/*.html", package_dir+"/examples/") - glob_move(package_dir+"/*.css", package_dir+"/examples/") - glob_move(package_dir+"/*.js", package_dir+"/examples/") - - print("Copying wx/ to package dir") - wx_subpackage_dir = os.path.abspath(installer_dir+"/../../wx/") - glob_copy(wx_subpackage_dir+"/*", package_dir+"/wx/") - - print("Moving wx examples from wx/examples to examples/wx") - glob_move(package_dir+"/wx/examples/*", package_dir+"/examples/wx/") - os.rmdir(package_dir+"/wx/examples/") - - print("Copying package dir examples to setup dir") - glob_copy(package_dir+"/examples/", setup_dir+"/examples/") - - # Create empty debug.log files so that package uninstalls cleanly - # in case examples were launched. Issue 149. - debug_log_dirs = [package_dir, - package_dir+"/examples/", - package_dir+"/examples/wx/"] - for dir in debug_log_dirs: - print("Creating empty debug.log in %s" % dir) - with open(dir+"/debug.log", "w") as f: - f.write("") - - print("Setup Package created successfully.") - -if __name__ == "__main__": - main() diff --git a/src/windows/installer/setup.cfg.template b/src/windows/installer/setup.cfg.template deleted file mode 100644 index 067dcbfd..00000000 --- a/src/windows/installer/setup.cfg.template +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -python-tag=cp%(PY_VERSION_DIGITS_ONLY)s diff --git a/src/windows/installer/setup.py.template b/src/windows/installer/setup.py.template deleted file mode 100644 index 36fb2809..00000000 --- a/src/windows/installer/setup.py.template +++ /dev/null @@ -1,68 +0,0 @@ -try: - # The setuptools package is not installed by default - # on a clean Ubuntu. Might be also a case on Windows. - # Python Eggs and Wheels can be created only with setuptools. - from setuptools import setup - from setuptools.command.install import install as _install - from setuptools.dist import Distribution - print("[setup.py] Using setuptools") -except: - from distutils.core import setup - from distutils.command.install import install as _install - from distutils.dist import Distribution - print("[setup.py] Using distutils") - -import sys -import os -import subprocess - -def post_install(): - """ Post install tasks """ - print("[setup.py] post_install()") - # Nothing extra is required to do on Windows. - -class install(_install): - def run(self): - _install.run(self) - post_install() - -class BinaryDistribution(Distribution): - def is_pure(self): - return False - -setup( - distclass=BinaryDistribution, - cmdclass={'install': install}, - name='cefpython3', # No spaces here, so that it works with deb packages. - version='%(APP_VERSION)s', - description='Python bindings for the Chromium Embedded Framework', - license='BSD 3-Clause', - author='Czarek Tomczak', - author_email='czarek.tomczak@gmail.com', - url='http://code.google.com/p/cefpython/', - platforms=['%(PLATFORM)s'], - packages=['cefpython3', 'cefpython3.wx'], - package_data={'cefpython3': [ - 'examples/*.py', - 'examples/*.html', - 'examples/*.js', - 'examples/*.css', - 'examples/wx/*.py', - 'examples/wx/*.html', - 'examples/wx/*.js', - 'examples/wx/*.css', - 'examples/wx/*.png', - 'locales/*.pak', - 'wx/*.txt', - 'wx/images/*.png', - '*.txt', - 'cefclient.exe', - 'subprocess.exe', - '*.pyd', - '*.dll', - '*.pak', - 'debug.log', - 'examples/debug.log', - 'examples/wx/debug.log', - ]} -) diff --git a/tools/automate.py b/tools/automate.py index 4da35108..0d966067 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -1,6 +1,22 @@ # Copyright (c) 2016 CEF Python, see the Authors file. All rights reserved. -"""Automates building CEF from sources with CEF Python patches applied. +""" +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. + +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) @@ -50,24 +66,6 @@ CEF_GIT_URL = "https://bitbucket.org/chromiumembedded/cef.git" -VS2015_VCVARS = "\"C:\Program Files (x86)\\Microsoft Visual Studio 14.0" \ - "\\VC\\vcvarsall.bat\" x86" \ - if ARCH32 else \ - "\"C:\Program Files (x86)\\Microsoft Visual Studio 14.0" \ - "\\VC\\vcvarsall.bat\" amd64" - -VS2013_VCVARS = "\"C:\Program Files (x86)\\Microsoft Visual Studio 12.0" \ - "\\VC\\vcvarsall.bat\" x86" \ - if ARCH32 else \ - "\"C:\Program Files (x86)\\Microsoft Visual Studio 12.0" \ - "\\VC\\cvarsall.bat\" amd64" - -VS2008_VCVARS = "\"%LocalAppData%\\Programs\\Common\\Microsoft" \ - "\\Visual C++ for Python\\9.0\\vcvarsall.bat\" x86" \ - if ARCH32 else \ - "\"%LocalAppData%\\Programs\\Common\\Microsoft" \ - "\\Visual C++ for Python\\9.0\\vcvarsall.bat\" amd64" - class Options(object): """Options from command-line and internal options.""" @@ -94,6 +92,9 @@ class Options(object): release_build = True build_type = "" # Will be set according to "release_build" value cef_binary = "" + build_cefclient_dir = "" + build_wrapper_mt_dir = "" + build_wrapper_md_dir = "" def main(): @@ -104,6 +105,10 @@ def main(): print(" automate-git.py works only with that version.") sys.exit(1) + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) + setup_options(docopt.docopt(__doc__)) if Options.build_cef: @@ -303,7 +308,7 @@ def build_cef_projects(): fix_cef_include_files() - # Find cef_binary directories and create the cef_binary/build/ dir + # Find cef_binary directory if not Options.cef_binary: if platform.system() == "Windows": files = glob.glob(os.path.join(Options.binary_distrib, @@ -324,127 +329,224 @@ def build_cef_projects(): assert os.path.exists(cef_binary) Options.cef_binary = cef_binary + # Set build directory + Options.build_cefclient_dir = os.path.join(Options.cef_binary, + "build_cefclient") + print("[automate.py] Creating build_cefclient dir in cef_binary dir") - build_cefclient = os.path.join(Options.cef_binary, "build_cefclient") + + # Check whether already built already_built = False - if os.path.exists(build_cefclient): + if build_cefclient_succeeded(): already_built = True + elif os.path.exists(Options.build_cefclient_dir): + # Last build failed, clean directory + assert Options.build_cefclient_dir + shutil.rmtree(Options.build_cefclient_dir) + os.makedirs(Options.build_cefclient_dir) else: - os.makedirs(build_cefclient) + os.makedirs(Options.build_cefclient_dir) # Build cefclient, cefsimple, ceftests if already_built: print("[automate.py] Already built: cefclient, cefsimple, ceftests") else: - print("[automate.py] Building cefclient, cefsimple, ceftests ...") + print("[automate.py] Build cefclient, cefsimple, ceftests") + # Cmake command = prepare_build_command() - command += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ - % Options.build_type - run_command(command, build_cefclient) + command.extend(["cmake", "-G", "Ninja", + "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) + run_command(command, Options.build_cefclient_dir) print("[automate.py] OK") - # On Linux cannot pass "&&" and run two commands using run_command() + # Ninja command = prepare_build_command() - command += "ninja cefclient cefsimple ceftests" - run_command(command, build_cefclient) + command.extend(["ninja", "cefclient", "cefsimple", "ceftests"]) + run_command(command, Options.build_cefclient_dir) print("[automate.py] OK") - if platform.system() == "Windows": - assert(os.path.exists(os.path.join(build_cefclient, - "tests", - "cefclient", - Options.build_type, - "cefclient.exe"))) - else: - assert (os.path.exists(os.path.join(build_cefclient, - "tests", - "cefclient", - Options.build_type, - "cefclient"))) + assert build_cefclient_succeeded() # Build libcef_dll_wrapper libs if platform.system() == "Windows": - build_wrapper_windows(Options.cef_binary) + build_wrapper_windows() -def prepare_build_command(build_lib=False): - """On Windows VS env variables must be set up by calling vcvarsall.bat""" - command = "" - if platform.system() == "Windows": - if build_lib: - msvs = get_msvs_for_python() - command = globals()["VS"+msvs+"_VCVARS"] + " && " - else: - if int(Options.cef_branch) >= 2704: - command = VS2015_VCVARS + " && " - else: - command = VS2013_VCVARS + " && " - return command +def build_wrapper_windows(): + # 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) - -def build_wrapper_windows(cef_binary): # Command to build libcef_dll_wrapper - wrapper_cmake = prepare_build_command(build_lib=True) - wrapper_cmake += "cmake -G \"Ninja\" -DCMAKE_BUILD_TYPE=%s .." \ - % Options.build_type + cmake_wrapper = prepare_build_command(build_lib=True) + cmake_wrapper.extend(["cmake", "-G", "Ninja", + "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) + + # Set build directory for /MT lib. + Options.build_wrapper_mt_dir = os.path.join(Options.cef_binary, + "build_wrapper_mt") - # Build libcef_dll_wrapper_mt.lib - build_wrapper_mt = os.path.join(cef_binary, "build_wrapper_mt") + # Check whether already built mt_already_built = False - if os.path.exists(build_wrapper_mt): + if build_wrapper_mt_succeeded(): mt_already_built = True + elif os.path.exists(Options.build_wrapper_mt_dir): + # Last build failed, clean directory + assert Options.build_wrapper_mt_dir + shutil.rmtree(Options.build_wrapper_mt_dir) + os.makedirs(Options.build_wrapper_mt_dir) else: - os.makedirs(build_wrapper_mt) + os.makedirs(Options.build_wrapper_mt_dir) + + # Build /MT lib. if mt_already_built: print("[automate.py] Already built: libcef_dll_wrapper /MT") else: - print("[automate.py] Building libcef_dll_wrapper /MT") + print("[automate.py] Build libcef_dll_wrapper /MT") old_gyp_msvs_version = Options.gyp_msvs_version Options.gyp_msvs_version = get_msvs_for_python() - run_command(wrapper_cmake, build_wrapper_mt) + # Cmake + run_command(cmake_wrapper, Options.build_wrapper_mt_dir) Options.gyp_msvs_version = old_gyp_msvs_version print("[automate.py] cmake OK") + # Ninja ninja_wrapper = prepare_build_command(build_lib=True) - ninja_wrapper += "ninja libcef_dll_wrapper" - run_command(ninja_wrapper, build_wrapper_mt) + ninja_wrapper.extend(["ninja", "libcef_dll_wrapper"]) + run_command(ninja_wrapper, Options.build_wrapper_mt_dir) print("[automate.py] ninja OK") - assert(os.path.exists(os.path.join(build_wrapper_mt, - "libcef_dll_wrapper", - "libcef_dll_wrapper.lib"))) + assert build_wrapper_mt_succeeded() - # Build libcef_dll_wrapper_md.lib - build_wrapper_md = os.path.join(cef_binary, "build_wrapper_md") + # Set build directory for /MD lib. + Options.build_wrapper_md_dir = os.path.join(Options.cef_binary, + "build_wrapper_md") + + # Check whether already built md_already_built = False - if os.path.exists(build_wrapper_md): + if build_wrapper_md_succeeded(): md_already_built = True + elif os.path.exists(Options.build_wrapper_md_dir): + # Last build failed, clean directory + assert Options.build_wrapper_md_dir + shutil.rmtree(Options.build_wrapper_md_dir) + os.makedirs(Options.build_wrapper_md_dir) else: - os.makedirs(build_wrapper_md) + os.makedirs(Options.build_wrapper_md_dir) + + # Build /MD lib. if md_already_built: print("[automate.py] Already built: libcef_dll_wrapper /MD") else: - print("[automate.py] Building libcef_dll_wrapper /MD") + print("[automate.py] Build libcef_dll_wrapper /MD") old_gyp_msvs_version = Options.gyp_msvs_version Options.gyp_msvs_version = get_msvs_for_python() - # Replace /MT with /MD /wd\"4275\" in CMakeLists.txt - # 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. - cmakelists = os.path.join(cef_binary, "CMakeLists.txt") - with open(cmakelists, "rb") as fp: - contents = fp.read() - contents = contents.replace(r"/MT ", r"/MD /wd\"4275\" ") - contents = contents.replace(r"/MTd ", r"/MDd /wd\"4275\" ") - with open(cmakelists, "wb") as fp: - fp.write(contents) - run_command(wrapper_cmake, build_wrapper_md) + # Fix cmake variables + # Cmake + fix_cmake_variables_for_md_library() + run_command(cmake_wrapper, Options.build_wrapper_md_dir) Options.gyp_msvs_version = old_gyp_msvs_version + fix_cmake_variables_for_md_library(undo=True) print("[automate.py] cmake OK") + # Ninja ninja_wrapper = prepare_build_command(build_lib=True) - ninja_wrapper += "ninja libcef_dll_wrapper" - run_command(ninja_wrapper, build_wrapper_md) + ninja_wrapper.extend(["ninja", "libcef_dll_wrapper"]) + run_command(ninja_wrapper, Options.build_wrapper_md_dir) print("[automate.py] ninja OK") - assert(os.path.exists(os.path.join(build_wrapper_md, - "libcef_dll_wrapper", - "libcef_dll_wrapper.lib"))) + assert build_wrapper_md_succeeded() + + +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() + + 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) + + +def build_cefclient_succeeded(): + """Whether building cefclient/cefsimple/ceftests succeeded.""" + assert Options.build_cefclient_dir + cefclient_exe = "cefclient.exe" if WINDOWS else "cefclient" + return os.path.exists(os.path.join(Options.build_cefclient_dir, + "tests", + "cefclient", + Options.build_type, + cefclient_exe)) + + +def build_wrapper_mt_succeeded(): + """Whether building /MT library succeeded (Windows-only).""" + assert Options.build_wrapper_mt_dir + return os.path.exists(os.path.join(Options.build_wrapper_mt_dir, + "libcef_dll_wrapper", + "libcef_dll_wrapper.lib")) + + +def build_wrapper_md_succeeded(): + """Whether building /MD library succeeded (Windows-only).""" + assert Options.build_wrapper_md_dir + return os.path.exists(os.path.join(Options.build_wrapper_md_dir, + "libcef_dll_wrapper", + "libcef_dll_wrapper.lib")) + + +def prepare_build_command(build_lib=False): + """On Windows VS env variables must be set up by calling vcvarsall.bat""" + command = list() + if platform.system() == "Windows": + if build_lib: + msvs = get_msvs_for_python() + command.append(globals()["VS"+msvs+"_VCVARS"]) + else: + if int(Options.cef_branch) >= 2704: + command.append(VS2015_VCVARS) + else: + command.append(VS2013_VCVARS) + command.append("&&") + return command def fix_cef_include_files(): @@ -600,13 +702,16 @@ def getenv(): return env -def run_command(command_line, working_dir): +def run_command(command, working_dir): """Run command in a given directory with env variables set. On Linux multiple commands on one line with the use of && are not allowed. """ - print("[automate.py] Running '"+command_line+"' in '" + + print("[automate.py] Running '"+" ".join(command)+"' in '" + working_dir+"'...") - args = shlex.split(command_line.replace("\\", "\\\\")) + if isinstance(command, str): + args = shlex.split(command.replace("\\", "\\\\")) + else: + args = command return subprocess.check_call(args, cwd=working_dir, env=getenv(), shell=(platform.system() == "Windows")) diff --git a/tools/build.py b/tools/build.py index 48c13874..6c424a78 100644 --- a/tools/build.py +++ b/tools/build.py @@ -1,16 +1,29 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under the BSD 3-clause license. + """ Build the cefpython module, install package and run example. +Before running you must first put cefpython ready CEF binaries and +libraries in the cefpython/build/ directory (create if doesn't exist). +You have two options for obtaining these binaries and libraries. + +Option 1: Download upstream CEF binaries and libraries from cefpython +GitHub Releases page. These binaries are tagged eg. "v55-upstream". +Extract the archive so that for example you have such a directory: +cefpython/build/cef55_3.2883.1553.g80bd606_win32/ . + +Option 2: Use the automate.py tool. With this tool you can build CEF +from sources or use ready binaries from Spotify Automated Builds. + Usage: - build.py VERSION [--debug] [--fast] + build.py VERSION [--rebuild-cpp] [--fast] [--kivy] Options: - VERSION Version in format xx.xx - --debug Debug mode - --fast Fast mode, don't delete C++ .o .a files, nor the setup/build/ - directory, and disable optimization flags when building - the so/pyd module. - --kivy Run Kivy example + VERSION Version in format xx.xx + --rebuild-cpp Force rebuild of C++ projects + --fast Fast mode + --kivy Run only Kivy example """ # How to debug on Linux: @@ -56,21 +69,18 @@ else: MODULE_EXT = "so" -# Compiler options -if MAC: - os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] - os.environ["CC"] = "gcc" - os.environ["CXX"] = "g++" - os.environ["CEF_CCFLAGS"] = "-arch x86_64" - os.environ["ARCHFLAGS"] = "-arch x86_64" - if ARCH32: - raise Exception("Python 32bit is not supported on Mac") +# First run +FIRST_RUN = False +CEFPYTHON_H = os.path.join(BUILD_CEFPYTHON, "cefpython.h") def main(): + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) print("[build.py] PYVERSION = %s" % PYVERSION) print("[build.py] OS_POSTFIX2 = %s" % OS_POSTFIX2) - setup_environ_path() + setup_environ() check_cython_version() command_line_args() check_directories() @@ -87,28 +97,57 @@ def main(): install_and_run() -def setup_environ_path(): - print("[build.py] Setup environment PATH") +def setup_environ(): + """Set environment variables. Set PATH so that it contains only + minimum set of directories,to avoid any possible issues. Set Python + include path. Set Mac compiler options. Etc.""" + print("[build.py] Setup environment variables") + if not WINDOWS: return - if ARCH32: - os.environ["PATH"] = ("C:\\Windows\\system32;C:\\Windows;" - "C:\\Windows\\System32\\Wbem;C:\\Python27") - else: - raise Exception("Only 32-bit is currently supported") - print("[build.py] PATH: {path}".format(path=os.environ["PATH"])) + # PATH + if WINDOWS: + path = [ + "C:\\Windows\\system32", + "C:\\Windows", + "C:\\Windows\\System32\\Wbem", + get_python_path(), + ] + os.environ["PATH"] = os.pathsep.join(path) + print("[build.py] environ PATH: {path}" + .format(path=os.environ["PATH"])) + + # INCLUDE env for vcproj build + if WINDOWS: + if "INCLUDE" not in os.environ: + os.environ["INCLUDE"] = "" + os.environ["INCLUDE"] += os.pathsep + os.path.join(get_python_path(), + "include") + print("[build.py] environ INCLUDE: {include}" + .format(include=os.environ["INCLUDE"])) + + # LIB env for vcproj build + if WINDOWS: + os.environ["AdditionalLibraryDirectories"] = os.path.join( + CEF_BINARIES_LIBRARIES, "lib") + print("[build.py] environ AdditionalLibraryDirectories: {lib}" + .format(lib=os.environ["AdditionalLibraryDirectories"])) - """ - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; - C:\Python27_x64;C:\Python27_amd64;C:\Python27_64 + # Mac compiler options + if MAC: + os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] + os.environ["CC"] = "gcc" + os.environ["CXX"] = "g++" + os.environ["CEF_CCFLAGS"] = "-arch x86_64" + os.environ["ARCHFLAGS"] = "-arch x86_64" + if ARCH32: + raise Exception("Python 32-bit is not supported on Mac") - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; - C:\Python34 - set PATH=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem; - C:\Python34_x64;C:\Python34_amd64;C:\Python34_64 - """ +def get_python_path(): + """Get Python path.""" + return os.path.dirname(sys.executable) def check_cython_version(): @@ -185,7 +224,7 @@ def check_directories(): # Check directories exist assert os.path.exists(BUILD_DIR) assert os.path.exists(BUILD_CEFPYTHON) - assert os.path.exists(CEF_BINARY) + assert os.path.exists(CEF_BINARIES_LIBRARIES) assert os.path.exists(CEFPYTHON_BINARY) @@ -204,31 +243,44 @@ def fix_cefpython_h(): content = ("%s\n\n" % pragma) + content with open("cefpython.h", "w") as fo: fo.write(content) - print("[build.py] Saved cefpython.h") + print("[build.py] Save build_cefpython/cefpython.h") def compile_cpp_projects_windows(): print("[build.py] Compile C++ projects") - print("[build.py] Build client_handler vcproj") + print("[build.py] ~~ Build CLIENT_HANDLER vcproj") vcproj = ("client_handler_py{pyver}_{os}.vcproj" .format(pyver=PYVERSION, os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "client_handler", vcproj) build_vcproj(vcproj) - print("[build.py] Build libcefpythonapp vcproj") + print("[build.py] ~~ Build LIBCEFPYTHONAPP vcproj") vcproj = ("libcefpythonapp_py{pyver}_{os}.vcproj" .format(pyver=PYVERSION, os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "subprocess", vcproj) build_vcproj(vcproj) - print("[build.py] Build subprocess vcproj") + print("[build.py] ~~ Build SUBPROCESS vcproj") vcproj = ("subprocess_{os}.vcproj" .format(os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "subprocess", vcproj) - build_vcproj(vcproj) - - print("[build.py] Build cpp_utils vcproj") + ret = build_vcproj(vcproj) + + # Copy subprocess executable + subprocess_from = os.path.join( + SUBPROCESS_DIR, + "Release_{os}".format(os=OS_POSTFIX2), + "subprocess_{os}.exe".format(os=OS_POSTFIX2)) + subprocess_to = os.path.join(CEFPYTHON_BINARY, "subprocess.exe") + if os.path.exists(subprocess_to): + os.remove(subprocess_to) + if ret == 0: + print("[build.py] Copy subprocess executable") + # shutil.copy() will also copy Permission bits + shutil.copy(subprocess_from, subprocess_to) + + print("[build.py] ~~ Build CPP_UTILS vcproj") vcproj = ("cpp_utils_{os}.vcproj" .format(os=OS_POSTFIX2)) vcproj = os.path.join(SRC_DIR, "cpp_utils", vcproj) @@ -236,11 +288,19 @@ def compile_cpp_projects_windows(): def build_vcproj(vcproj): + if not os.path.exists(CEFPYTHON_H): + print("[build.py] INFO: Looks like first run, as cefpython.h" + " is missing. Skip building C++ project.") + global FIRST_RUN + FIRST_RUN = True + return + if PYVERSION == "27": args = list() - args.append("%LocalAppData%\\Programs\\Common\\" - "Microsoft\\Visual C++ for Python\\9.0\\" - "VC\\bin\\amd64\\vcbuild.exe") + args.append(VS2008_VCVARS) + args.append(VS_PLATFORM_ARG) + args.append("&&") + args.append(VS2008_BUILD) args.append("/nocolor") args.append("/nologo") args.append("/nohtmllog") @@ -250,20 +310,17 @@ def build_vcproj(vcproj): ret = subprocess.call(args, shell=True) if ret != 0: compile_ask_to_continue() + return ret else: raise Exception("Only Python 2.7 32-bit is currently supported") - """ - In VS2010 vcbuild was replaced by msbuild.exe. - /clp:disableconsolecolor - msbuild /p:BuildProjectReferences=false project.proj - MSBuild.exe MyProject.proj /t:build - """ + # In VS2010 vcbuild was replaced by msbuild.exe. + # /clp:disableconsolecolor + # msbuild /p:BuildProjectReferences=false project.proj + # MSBuild.exe MyProject.proj /t:build def compile_ask_to_continue(): - print("[build.py] **INFO**: On first run you should continue despite" - " errors and after completion re-run the build.py script again") # noinspection PyUnboundLocalVariable what = input("[build.py] make failed, 'y' to continue, Enter to stop: ") if what != "y": @@ -280,17 +337,8 @@ def compile_cpp_projects_unix(): # fails and then run the compile.py script again and this time # make should succeed. - # -------- CPP_UTILS_DIR - - os.chdir(CPP_UTILS_DIR) - if not FAST_FLAG: - subprocess.call("rm -f *.o *.a", shell=True) - - ret = subprocess.call("make -f Makefile", shell=True) - if ret != 0: - compile_ask_to_continue() - - # -------- CLIENT_HANDLER_DIR + # -- CLIENT_HANDLER + print("[build.py] ~~ Build CLIENT_HANDLER project") os.chdir(CLIENT_HANDLER_DIR) if not FAST_FLAG: @@ -300,7 +348,8 @@ def compile_cpp_projects_unix(): if ret != 0: compile_ask_to_continue() - # -------- SUBPROCESS_DIR + # -- LIBCEFPYTHONAPP + print("[build.py] ~~ Build LIBCEFPYTHONAPP project") os.chdir(SUBPROCESS_DIR) if not FAST_FLAG: @@ -311,14 +360,29 @@ def compile_cpp_projects_unix(): if ret != 0: compile_ask_to_continue() + # -- SUBPROCESS + print("[build.py] ~~ Build SUBPROCESS project") ret = subprocess.call("make -f Makefile", shell=True) if ret != 0: compile_ask_to_continue() - subprocess_exe = os.path.join(CEFPYTHON_BINARY, "subprocess") - if os.path.exists("./subprocess"): - # .copy() will also copy Permission bits - shutil.copy("./subprocess", subprocess_exe) + # Copy subprocess executable + subprocess_from = os.path.join(SUBPROCESS_DIR, "subprocess") + subprocess_to = os.path.join(CEFPYTHON_BINARY, "subprocess") + if os.path.exists(subprocess_from): + # shutil.copy() will also copy Permission bits + shutil.copy(subprocess_from, subprocess_to) + + # -- CPP_UTILS + print("[build.py] ~~ Build CPP_UTILS project") + + os.chdir(CPP_UTILS_DIR) + if not FAST_FLAG: + subprocess.call("rm -f *.o *.a", shell=True) + + ret = subprocess.call("make -f Makefile", shell=True) + if ret != 0: + compile_ask_to_continue() def clear_cache(): @@ -510,7 +574,19 @@ def build_cefpython_module(): # Check if built succeeded after pyx files were removed if ret != 0: - print("[build.py] FAILED to build the cefpython module") + if FIRST_RUN and os.path.exists(CEFPYTHON_H): + print("[build.py] INFO: looks like this was first run and" + " linking is expected to fail in such case. Will re-run" + " the build.py script programmatically now.") + args = list() + args.append(sys.executable) + args.append(os.path.join(TOOLS_DIR, os.path.basename(__file__))) + assert __file__ in sys.argv[0] + args.extend(sys.argv[1:]) + ret = subprocess.call(args, shell=True) + sys.exit(ret) + else: + print("[build.py] ERROR: failed to build the cefpython module") sys.exit(1) # Move the cefpython module @@ -521,7 +597,7 @@ def build_cefpython_module(): .format(pyver=PYVERSION, ext=MODULE_EXT))) - print("[build.py] Done building the cefpython module") + print("[build.py] DONE building the cefpython module") def move_file_by_pattern(pattern, move_to): @@ -553,60 +629,44 @@ def delete_directories_by_pattern(pattern): def install_and_run(): - os.chdir(BUILD_CEFPYTHON) - # if DEBUG_FLAG: # os.chdir("./binaries_%s" % BITS) # subprocess.call("cygdb . --args python-dbg wxpython.py", shell=True) print("[build.py] Install and run...") + os.chdir(BUILD_DIR) - # Clean installer directory from previous run - try: - delete_directories_by_pattern("./cefpython3-{ver}-*-setup/" - .format(ver=VERSION)) - except: - if LINUX: - os.system("sudo rm -rf ./cefpython3-{ver}-*-setup/" - .format(ver=VERSION)) - else: - raise + # Setup installer directory + setup_installer_dir = ("./cefpython3-{version}-{os}-setup/" + .format(version=VERSION, os=OS_POSTFIX2)) + setup_installer_dir = os.path.join(BUILD_DIR, setup_installer_dir) - # System python requires sudo when installing package - if sys.executable in ["/usr/bin/python", "/usr/bin/python3"]: - sudo = "sudo" - else: - sudo = "" - - os.chdir(BUILD_CEFPYTHON) + # Delete setup installer directory if exists + if os.path.exists(setup_installer_dir): + delete_directory_reliably(setup_installer_dir) # Make setup installer print("[build.py] Make setup installer") - os.system("{python} ../../tools/setup/make.py --version {ver}" - .format(python=sys.executable, ver=VERSION)) - - # Enter setup installer directory - os.chdir("cefpython3-{ver}-{os_postfix2}-setup/" - .format(ver=VERSION, os_postfix2=OS_POSTFIX2)) + make_tool = os.path.join(TOOLS_DIR, "make_installer.py") + os.system("{python} {make_tool} --version {version}" + .format(python=sys.executable, + make_tool=make_tool, + version=VERSION)) # Install print("[build.py] Install the cefpython package") + os.chdir(setup_installer_dir) os.system("{sudo} {python} setup.py install" - .format(sudo=sudo, python=sys.executable)) - - # Delete setup installer directory - print("[build.py] Delete the setup installer directory") - if WINDOWS: - shutil.rmtree("./cefpython3-{ver}-{os_postfix2}-setup/" - .format(ver=VERSION, os_postfix2=OS_POSTFIX2)) - else: - os.system("{sudo} rm -rf ./cefpython3-{ver}-*-setup/" - .format(sudo=sudo, ver=VERSION)) + .format(sudo=get_sudo(), python=sys.executable)) + os.chdir(BUILD_DIR) # Run unittests print("[build.py] Run unittests") - os.system("cd {unittests_dir} && {python} _test_runner.py" - .format(unittests_dir=UNITTESTS_DIR, python=sys.executable)) + test_runner = os.path.join(UNITTESTS_DIR, "_test_runner.py") + ret = os.system("{python} {test_runner}" + .format(python=sys.executable, test_runner=test_runner)) + if ret != 0: + sys.exit(ret) # Run examples print("[build.py] Run examples") @@ -625,14 +685,38 @@ def install_and_run(): if LINUX: run_examples += (" && {python}" " {linux_dir}/deprecated_64bit/kivy_.py") - run_examples.format(linux_dir=LINUX_DIR, examples_dir=EXAMPLES_DIR) + run_examples.format( + python=sys.executable, + linux_dir=LINUX_DIR, + examples_dir=EXAMPLES_DIR) os.system(run_examples) - # Enter tools dir - os.system("cd {tools_dir}".format(tools_dir=TOOLS_DIR)) - print("[build.py] DONE") +def get_sudo(): + # System Python requires sudo when installing package + if sys.executable in ["/usr/bin/python", "/usr/bin/python3"]: + sudo = "sudo" + else: + sudo = "" + return sudo + + +def delete_directory_reliably(adir): + assert len(adir) > 2 + assert os.path.isdir(adir) + print("[build.py] Delete directory: {dir}" + .format(dir=adir.replace(ROOT_DIR, ""))) + if WINDOWS: + shutil.rmtree(adir) + else: + # On Linux sudo might be required to delete directory, as this + # might be a setup installer directory with package installed + # using sudo and in such case files were created with sudo. + os.system("{sudo} rm -rf {dir}" + .format(sudo=get_sudo(), dir=adir)) + + if __name__ == "__main__": main() diff --git a/tools/build_module.py b/tools/build_module.py index bb99eb2f..0d1524df 100644 --- a/tools/build_module.py +++ b/tools/build_module.py @@ -1,5 +1,10 @@ -# For internal use only - called by build.py. -# This is Cython's setup for building the cefpython module. +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under the BSD 3-clause license. + +""" +build_module.py is for internal use only - called by build.py. +This is Cython's setup for building the cefpython module +""" # Use setuptools so that "Visual C++ compiler for Python 2.7" tools # can be used. Otherwise "Unable to find vcvarsall.bat" error occurs. @@ -171,7 +176,7 @@ def get_include_dirs(): def get_library_dirs(): print("[build_module.py] Prepare library directories") library_dirs = [ - os.path.join(CEF_BINARY, "lib"), + os.path.join(CEF_BINARIES_LIBRARIES, "lib"), ] if WINDOWS: library_dirs.extend([ @@ -182,8 +187,11 @@ def get_library_dirs(): os.path.join(SRC_DIR, "subprocess", "Release_{os}" .format(os=OS_POSTFIX2)), + os.path.join(SRC_DIR, "subprocess", + "Release_py{pyver}_{os}" + .format(pyver=PYVERSION, os=OS_POSTFIX2)), os.path.join(SRC_DIR, "cpp_utils", - "Release_py{os}" + "Release_{os}" .format(os=OS_POSTFIX2)) ]) if MAC or LINUX: @@ -280,6 +288,9 @@ def compile_time_constants(): def main(): + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) print("[build_module.py] Cython version: %s" % Cython.__version__) compile_time_constants() options = dict() diff --git a/tools/common.py b/tools/common.py index f42a2667..fb0a733b 100644 --- a/tools/common.py +++ b/tools/common.py @@ -29,32 +29,55 @@ # Python version eg. 27 PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) -# Directories -TOOLS_DIR = os.path.abspath(os.path.dirname(__file__)) -SRC_DIR = os.path.abspath(os.path.join(TOOLS_DIR, "../src")) -WINDOWS_DIR = os.path.abspath(os.path.join(SRC_DIR, "windows")) -MAC_DIR = os.path.abspath(os.path.join(SRC_DIR, "mac")) -LINUX_DIR = os.path.abspath(os.path.join(SRC_DIR, "linux")) -CPP_UTILS_DIR = os.path.abspath(os.path.join(SRC_DIR, "cpp_utils")) -CLIENT_HANDLER_DIR = os.path.abspath(os.path.join(SRC_DIR, "client_handler")) -SUBPROCESS_DIR = os.path.abspath(os.path.join(SRC_DIR, "subprocess")) -CEFPYTHON_DIR = os.path.abspath(os.path.join(SRC_DIR, "..")) -EXAMPLES_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "examples")) -UNITTESTS_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "unittests")) -BUILD_DIR = os.path.abspath(os.path.join(CEFPYTHON_DIR, "build")) -BUILD_CEFPYTHON = os.path.abspath(os.path.join(BUILD_DIR, "build_cefpython")) -# CEF_BINARY may be auto-overwritten through detect_cef_binary_directory() -CEF_BINARY = os.path.abspath(os.path.join(BUILD_DIR, "cef_"+OS_POSTFIX2)) -CEFPYTHON_BINARY = os.path.abspath(os.path.join(BUILD_DIR, - "cefpython_"+OS_POSTFIX2)) - - -def detect_cef_binary_directory(): - # Detect cef binary directory created by automate.py - # eg. build/cef55_3.2883.1553.g80bd606_win32/ - # and set CEF_BINARY to it. Otherwise CEF_BINARY will - # indicate to build/cef_{os_postfix2}/. - if not os.path.exists(CEF_BINARY): +# Root directory +assert __file__ +ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + +# Other directories +BUILD_DIR = os.path.join(ROOT_DIR, "build") +if BUILD_DIR: + BUILD_CEFPYTHON = os.path.join(BUILD_DIR, "build_cefpython") + # -- Auto-detected directories. + # May be auto-overwritten through detect_cef_binaries_libraries_dir() + CEF_BINARIES_LIBRARIES = os.path.join(BUILD_DIR, "cef_"+OS_POSTFIX2) + # Will be overwritten through detect_cefpython_binary_dir() + CEFPYTHON_BINARY = "CEFPYTHON_BINARY" +EXAMPLES_DIR = os.path.join(ROOT_DIR, "examples") +SRC_DIR = os.path.join(ROOT_DIR, "src") +if SRC_DIR: + CLIENT_HANDLER_DIR = os.path.join(SRC_DIR, "client_handler") + CPP_UTILS_DIR = os.path.join(SRC_DIR, "cpp_utils") + LINUX_DIR = os.path.join(SRC_DIR, "linux") + MAC_DIR = os.path.join(SRC_DIR, "mac") + SUBPROCESS_DIR = os.path.join(SRC_DIR, "subprocess") + WINDOWS_DIR = os.path.abspath(os.path.join(SRC_DIR, "windows")) +TOOLS_DIR = os.path.join(ROOT_DIR, "tools") +if TOOLS_DIR: + INSTALLER_DIR = os.path.join(TOOLS_DIR, "installer") +UNITTESTS_DIR = os.path.abspath(os.path.join(ROOT_DIR, "unittests")) + +# Visual Studio constants +VS_PLATFORM_ARG = "x86" if ARCH32 else "amd64" +VS2015_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 14.0" + "\\VC\\vcvarsall.bat") +VS2015_BUILD = "" # TODO +VS2013_VCVARS = ("C:\\Program Files (x86)\\Microsoft Visual Studio 12.0" + "\\VC\\vcvarsall.bat") +VS2013_BUILD = "" # TODO +VS2008_VCVARS = ("%LocalAppData%\\Programs\\Common\\Microsoft" + "\\Visual C++ for Python\\9.0\\vcvarsall.bat") +VS2008_BUILD = ("%LocalAppData%\\Programs\\Common\\" + "Microsoft\\Visual C++ for Python\\9.0\\" + "VC\\bin\\amd64\\vcbuild.exe") + + +def detect_cef_binaries_libraries_dir(): + """Detect cef binary directory created by automate.py + eg. build/cef55_3.2883.1553.g80bd606_win32/ + and set CEF_BINARIES_LIBRARIES to it, otherwise it will + point to eg. build/cef_win32/ .""" + global CEF_BINARIES_LIBRARIES + if not os.path.exists(CEF_BINARIES_LIBRARIES): version = get_cefpython_version() dirs = glob.glob(os.path.join( BUILD_DIR, @@ -64,10 +87,19 @@ def detect_cef_binary_directory(): os=OS_POSTFIX2, sep=os.sep))) if len(dirs) == 1: - print("[common.py] Auto detected CEF_BINARY directory: {dir}" - .format(dir=dirs[0])) - global CEF_BINARY - CEF_BINARY = dirs[0] + CEF_BINARIES_LIBRARIES = os.path.normpath(dirs[0]) + + +def detect_cefpython_binary_dir(): + """Detect cefpython binary directory where cefpython modules + will be put. Eg. buil/cefpython55_win32/.""" + version = get_cefpython_version() + binary_dir = "cefpython{major}_{os}".format( + major=version["CHROME_VERSION_MAJOR"], + os=OS_POSTFIX2) + binary_dir = os.path.join(BUILD_DIR, binary_dir) + global CEFPYTHON_BINARY + CEFPYTHON_BINARY = binary_dir def get_cefpython_version(): @@ -88,4 +120,5 @@ def get_version_from_file(header_file): return ret -detect_cef_binary_directory() +detect_cef_binaries_libraries_dir() +detect_cefpython_binary_dir() diff --git a/tools/installer/cefpython3.README.txt b/tools/installer/cefpython3.README.txt new file mode 100644 index 00000000..d23dea09 --- /dev/null +++ b/tools/installer/cefpython3.README.txt @@ -0,0 +1,13 @@ +1. To install CEF Python 3 package type: + + python setup.py install + + On Linux/Mac if using system Python then you need to add sudo: + + sudo python setup.py install + +2. To run examples enter the examples/ directory, + start with the hello_world.py example: + + cd examples/ + python hello_world.py diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py new file mode 100644 index 00000000..ca54b44b --- /dev/null +++ b/tools/installer/cefpython3.__init__.py @@ -0,0 +1,62 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under BSD 3-clause license. + +# NOTE: Template variables like {{VERSION}} are replaced with actual +# values when make.py tool generates this package installer. + +import os +import sys +import ctypes +import platform + +__all__ = ["cefpython", "wx"] +__version__ = "{{VERSION}}" +__author__ = "The CEF Python authors" + +# If package was installed using PIP or setup.py then package +# dir is here: +# /usr/local/lib/python2.7/dist-packages/cefpython3/ + +# If this is a debian package then package_dir returns: +# /usr/lib/pymodules/python2.7/cefpython3 +# The above path consists of symbolic links to the real directory: +# /usr/share/pyshared/cefpython3 + +package_dir = os.path.dirname(os.path.abspath(__file__)) + +# This loads the libcef.so library for the subprocess executable. +# On Mac it works without setting library paths, but let's set it +# just to be sure. +os.environ["LD_LIBRARY_PATH"] = package_dir +os.environ["DYLD_LIBRARY_PATH"] = package_dir + +# This env variable will be returned by cefpython.GetModuleDirectory(). +os.environ["CEFPYTHON3_PATH"] = package_dir + +# This loads the libcef library for the main python executable. +# This is required only on linux and Mac. +# The libffmpegsumo.so library does not need to be loaded here, +# it may cause issues to load it here in the browser process. +libcef = None +if platform.system() == "Darwin": + libcef = os.path.join(package_dir, "libcef.dylib") +elif platform.system() == "Linux": + libcef = os.path.join(package_dir, "libcef.so") +if libcef: + ctypes.CDLL(libcef, ctypes.RTLD_GLOBAL) + +# Load the cefpython module for proper Python version +if (2, 7) <= sys.version_info < (2, 8): + # noinspection PyUnresolvedReferences + from . import cefpython_py27 as cefpython +elif (3, 4) <= sys.version_info < (3, 5): + # noinspection PyUnresolvedReferences + from . import cefpython_py34 as cefpython +elif (3, 5) <= sys.version_info < (3, 6): + # noinspection PyUnresolvedReferences + from . import cefpython_py35 as cefpython +elif (3, 6) <= sys.version_info < (3, 7): + # noinspection PyUnresolvedReferences + from . import cefpython_py36 as cefpython +else: + raise Exception("Python version not supported: " + sys.version) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py new file mode 100644 index 00000000..646c5fb3 --- /dev/null +++ b/tools/installer/cefpython3.setup.py @@ -0,0 +1,225 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under BSD 3-clause license. + +""" +cefpython3 package setup.py file. + +Usage: + setup.py install + setup.py bdist_wheel [--universal] + +Options: + install Install package + bdist_wheel Generate wheel package. Use the --universal flag when + you have built cefpython modules for multiple Python + versions. +""" + +# NOTE: Template variables like {{VERSION}} are replaced with actual +# values when make.py tool generates this package installer. + +import copy +import os +import platform +import subprocess +import sys + +# The setuptools package is not installed by default on a clean +# Ubuntu. Might be also a case on Windows. Also Python Eggs +# and Wheels can be created only with setuptools. +try: + from setuptools import setup + from setuptools.command.install import install + from setuptools.dist import Distribution + print("[setup.py] Using setuptools") +except ImportError: + from distutils.core import setup + from distutils.command.install import install + from distutils.dist import Distribution + print("[setup.py] Using distutils") + if "bdist_wheel" in sys.argv: + print("[setup.py] ERROR: You must install setuptools package using" + " pip tool to be able to create a wheel package. Type" + " 'pip install setuptools'.") + sys.exit(1) + + +# Need to know which files are executables to set appropriate execute +# permissions during post_install_hook. On Windows .exe postfix will +# be added to these automatically. +EXECUTABLES_NOEXT = [ + "cefclient", + "cefsimple", + "ceftests", + "subprocess", +] + + +class custom_install(install): + def __init__(self, *args, **kwargs): + install.__init__(self, *args, **kwargs) + + def run(self): + install.run(self) + post_install_hook() + + +class BinaryDistribution(Distribution): + def is_pure(self): + return False + + +# Provide a custom install command +print("[setup.py] Overload install command to enable execution of" + " post install hook") +cmdclass = {"install": custom_install} + +# Set custom platform tags on Mac when generating wheel package. See: +# http://lepture.com/en/2014/python-on-a-hard-wheel +if platform.system() == "Darwin" and "bdist_wheel" in sys.argv: + print("[setup.py] Overload bdist_wheel command to add custom" + " platform tags") + from wheel.bdist_wheel import bdist_wheel + + class custom_bdist_wheel(bdist_wheel): + def get_tag(self): + tag = bdist_wheel.get_tag(self) + platform_tag = ("macosx_10_6_intel" + ".macosx_10_9_intel.macosx_10_9_x86_64" + ".macosx_10_10_intel.macosx_10_10_x86_64") + tag = (tag[0], tag[1], platform_tag) + return tag + + cmdclass["bdist_wheel"] = custom_bdist_wheel + + +def main(): + setup( + distclass=BinaryDistribution, + cmdclass=cmdclass, + name="cefpython3", # No spaces here, so that it works with deb pkg + version="{{VERSION}}", + description="GUI toolkit for embedding a Chromium widget" + " in desktop applications", + license="BSD 3-clause", + author="Czarek Tomczak", + author_email="czarek.tomczak@@gmail.com", + url="https://github.com/cztomczak/cefpython", + download_url="https://github.com/cztomczak/cefpython/releases", + platforms=["{{SYSCONFIG_PLATFORM}}"], + packages=["cefpython3"], # Disabled: "cefpython3.wx" + package_data=get_package_data(), + classifiers=[ + "Development Status :: 6 - Mature", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Natural Language :: English", + "Operating System :: MacOS :: MacOS X", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Topic :: Desktop Environment", + "Topic :: Internet", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: Browsers", + "Topic :: Multimedia", + ("Topic :: Software Development :: Libraries" + ":: Application Frameworks"), + "Topic :: Software Development :: User Interfaces", + ], + ) + print("[setup.py] OK installed") + + +def get_package_data(): + package_data = {"cefpython3": get_package_files()} + return package_data + + +def get_package_files(relative_dir=".", recursive=False): + """Finds files recursively in the cefpython3/ local directory. + Includes only files and their paths are relative to the cefpython3/ + local directory. Empty directories are not included.""" + old_dir = None + if not recursive: + old_dir = os.getcwd() + setup_dir = os.path.abspath(os.path.dirname(__file__)) + local_pkg_dir = os.path.join(setup_dir, "cefpython3") + os.chdir(local_pkg_dir) + files = os.listdir(relative_dir) + ret = list() + for fpath in files: + fpath = os.path.join(relative_dir, fpath) + if os.path.isdir(fpath): + ret.extend(get_package_files(relative_dir=fpath, recursive=True)) + else: + ret.append(fpath) + if not recursive: + os.chdir(old_dir) + return ret + + +def get_executables(): + data = copy.copy(EXECUTABLES_NOEXT) + if platform.system() == "Windows": + for key, executable in enumerate(data): + data[key] += ".exe" + return data + + +def post_install_hook(): + """Post install hook to chmod files on Linux and Mac.""" + + # Nothing extra required to do on Windows + if platform.system() == "Windows": + print("[setup.py] post_install_hook is ignored on Windows") + return + + # If this a wheel package generation then do not execute the hook + if "bdist_wheel" in sys.argv: + print("[setup.py] Ignoring post_install_hook as this is bdist_wheel") + return + + print("[setup.py] Execute post_install_hook") + + # Find the installed package directory. Do not import from + # the local cefpython3/ directory. + print("[setup.py] Overload sys.path to facilitate finding correct" + " directory for the installed package") + del sys.path[0] + sys.path.append("") + import cefpython3 + installed_package_dir = os.path.dirname(cefpython3.__file__) + + # Make sure that the imported package wasn't the local cefptyhon3/ + # directory. + print("[setup.py] Installed package directory: {dir}" + .format(dir=installed_package_dir)) + assert not installed_package_dir.startswith( + os.path.dirname(os.path.abspath(__file__))) + + # Set permissions on executables + print("[setup.py] Set execute permissions on executables") + for executable in get_executables(): + executable = os.path.join(installed_package_dir, executable) + command = "chmod +x {executable}".format(executable=executable) + print("[setup.py] {command}".format(command=command)) + subprocess.call(command, shell=True) + + # Set write permissions on log files + print("[setup.py] Set write permissions on log files") + package_data = get_package_data() + for pkgfile in package_data: + if not pkgfile.endswith(".log"): + continue + logfile = os.path.join(installed_package_dir, pkgfile) + command = "chmod 666 {logfile}".format(logfile=logfile) + print("[setup.py] {command}".format(command=command)) + subprocess.call(command, shell=True) + + +if __name__ == "__main__": + main() diff --git a/tools/make_installer.py b/tools/make_installer.py new file mode 100644 index 00000000..87f1a12d --- /dev/null +++ b/tools/make_installer.py @@ -0,0 +1,309 @@ +# Copyright (c) 2017 The CEF Python authors. All rights reserved. +# Licensed under BSD 3-clause license. + +""" +Create setup.py package installer. + +Usage: + make.py VERSION + +Options: + VERSION Version number eg. 50.0 +""" + +from common import * + +import glob +import os +import re +import shutil +import subprocess +import sys +import sysconfig + +# Command line args +VERSION = "" + +# Globals +SETUP_DIR = "" +PKG_DIR = "" + +# Config +IGNORE_EXT = [".log", ".pyc", ".pdb", ] +IGNORE_DIRS = ["__pycache__"] + + +def main(): + command_line_args() + + # Setup and package directories + global SETUP_DIR, PKG_DIR + setup_dir_name = ("cefpython3-{version}-{os}-setup" + .format(version=VERSION, os=OS_POSTFIX2)) + SETUP_DIR = os.path.join(BUILD_DIR, setup_dir_name) + PKG_DIR = os.path.join(SETUP_DIR, "cefpython3") + + # Print src and dest for file operations + print("[make_installer.py] Src: {src}".format(src=ROOT_DIR)) + print("[make_installer.py] Dst: {dst}".format(dst=SETUP_DIR)) + + # Make directories + if os.path.exists(SETUP_DIR): + print("[make_installer.py] Delete: {dir}" + .format(dir=SETUP_DIR.replace(ROOT_DIR, ""))) + shutil.rmtree(SETUP_DIR) + os.makedirs(SETUP_DIR) + os.makedirs(os.path.join(SETUP_DIR, "examples/")) + os.makedirs(PKG_DIR) + os.makedirs(os.path.join(PKG_DIR, "examples/")) + + # Copy files from tools/installer/ + copy_tools_installer_files(SETUP_DIR, PKG_DIR) + + # Multiple copy operations using glob patterns + copy_operations = [ + (ROOT_DIR, "License"), (PKG_DIR,), + (CEF_BINARIES_LIBRARIES, "*.txt"), (PKG_DIR,), + (CEF_BINARIES_LIBRARIES, "bin/*"), (PKG_DIR,), + (CEFPYTHON_BINARY, "*"), (PKG_DIR,), + (EXAMPLES_DIR, "*"), (PKG_DIR, "examples/"), + (EXAMPLES_DIR, "*"), (SETUP_DIR, "examples/"), + ] + perform_copy_operations(copy_operations) + + # Linux only operations + if LINUX: + copy_operations_linux = [ + (LINUX_DIR, "binaries_64bit/kivy_.py"), + (PKG_DIR, "examples/"), + (LINUX_DIR, "binaries_64bit/kivy-select-boxes/*"), + (PKG_DIR, "examples/") + ] + perform_copy_operations(copy_operations_linux) + + # Create empty debug.log files so that package uninstalls cleanly + # in case examples or CEF tests were launched. See Issue #149. + create_empty_log_file(os.path.join(PKG_DIR, "debug.log")) + create_empty_log_file(os.path.join(PKG_DIR, "examples/debug.log")) + + print("[make_installer.py] DONE. Installer package created: {setup_dir}" + .format(setup_dir=SETUP_DIR)) + + +def command_line_args(): + args = " ".join(sys.argv) + match = re.search(r"\d+\.\d+", args) + if match: + global VERSION + VERSION = match.group(0) + else: + print(__doc__) + sys.exit(1) + + +def copy_tools_installer_files(setup_dir, pkg_dir): + variables = dict() + variables["VERSION"] = VERSION + variables["SYSCONFIG_PLATFORM"] = sysconfig.get_platform() + + shutil.copy( + os.path.join(INSTALLER_DIR, "cefpython3.README.txt"), + os.path.join(setup_dir, "README.txt")) + + copy_template_file( + os.path.join(INSTALLER_DIR, "cefpython3.setup.py"), + os.path.join(setup_dir, "setup.py"), + variables) + + copy_template_file( + os.path.join(INSTALLER_DIR, "cefpython3.__init__.py"), + os.path.join(pkg_dir, "__init__.py"), + variables) + + +def copy_template_file(src, dst, variables): + """Copy file and replaces template variables in that file.""" + print("[make_installer.py] Copy_t: {src} ==> {dst}" + .format(src=short_src_path(src), dst=short_dst_path(dst))) + with open(src, "rb") as fo: + contents = fo.read() + contents = replace_template_vars(contents, variables) + with open(dst, "wb") as fo: + fo.write(contents) + return contents + + +def replace_template_vars(string, dictionary): + """Replaces template variables like {{SOME}} in the string + using the dictionary values.""" + orig_string = string + for key, value in dictionary.items(): + string = string.replace("{{"+key+"}}", value) + if string == orig_string: + raise Exception("Nothing to format") + if re.search(r"\{\{[a-zA-Z0-9_]+\}\}", string): + raise Exception("Not all strings were formatted") + return string + + +def perform_copy_operations(operations): + assert len(operations) % 2 == 0 + count_ops = int(len(operations) / 2) + for op_i in range(count_ops): + # Refer to values by index + pattern = operations[op_i*2] + dst_dir = operations[op_i*2+1] + # Convert tuples to lists + pattern = list(pattern) + dst_dir = list(dst_dir) + # Join paths + pattern = os.path.join(*pattern) + dst_dir = os.path.join(*dst_dir) + dst_dir = os.path.abspath(dst_dir) + # Normalize unix slashes on Windows + pattern = pattern.replace("/", os.path.sep) + # dst_dir must be a directory + if not os.path.isdir(dst_dir): + raise Exception("Not a directory: {dst_dir}" + .format(dst_dir=dst_dir)) + # Is pattern a file or a directory + if os.path.isfile(pattern): + if is_ignored_path(pattern): + raise Exception("Copy operation pattern is in ignore list:" + " {pattern}".format(pattern=pattern)) + print("[make_installer.py] Copy: {file} ==> {dir}" + .format(file=short_src_path(pattern), + dir=short_dst_path(dst_dir))) + # Destination file must not exist + assert not os.path.exists(os.path.join(dst_dir, + os.path.basename(pattern))) + shutil.copy(pattern, dst_dir) + else: + # pattern is a glob pattern + base_dir = os.path.dirname(pattern) + assert base_dir + assert base_dir == os.path.abspath(base_dir) + paths = glob.glob(pattern) + if not len(paths): + raise Exception("No paths found in: {pattern}" + .format(pattern=pattern)) + for path in paths: + # "path" variable contains absolute path + assert path == os.path.abspath(path) + if os.path.isfile(path): + if is_ignored_path(path): + continue + print("[make_installer.py] Copy: {file} ==> {dir}" + .format(file=short_src_path(path), + dir=short_dst_path(dst_dir))) + # Destination file must not exist + assert not os.path.exists( + os.path.join(dst_dir, os.path.basename(path))) + shutil.copy(path, dst_dir) + elif os.path.isdir(path): + if is_ignored_path(path): + continue + relative_dir = path.replace(base_dir, "") + assert relative_dir[0] == os.path.sep + relative_dir = relative_dir[1:] + perform_copy_recursively(base_dir, relative_dir, dst_dir) + else: + raise Exception("Unknown path: {path}".format(path=path)) + + +def perform_copy_recursively(base_dir, relative_dir, new_dir): + real_dir = os.path.join(base_dir, relative_dir) + assert os.path.exists(real_dir) and os.path.isdir(real_dir) + assert os.path.exists(new_dir) and os.path.isdir(new_dir) + + # Create subdirectory + new_subdir = os.path.join(new_dir, relative_dir) + if not os.path.exists(new_subdir): + print("[make_installer.py] Create: {dir}" + .format(dir=short_dst_path(new_subdir))) + os.makedirs(new_subdir) + + # List directory + paths = os.listdir(real_dir) + for path in paths: + # "path" variable contains relative path + real_path = os.path.join(real_dir, path) + path = os.path.join(relative_dir, path) + if os.path.isdir(real_path): + if is_ignored_path(real_path): + continue + perform_copy_recursively(base_dir, path, new_dir) + elif os.path.isfile(real_path): + if is_ignored_path(real_path): + continue + new_file = os.path.join(new_dir, path) + new_subdir = os.path.dirname(new_file) + if os.path.exists(new_file): + raise Exception("Path aready exists: {new_file}" + .format(new_file=short_dst_path(new_file))) + print("[make_installer.py] Copy: {file} ==> {dir}" + .format(file=short_src_path(real_path), + dir=short_dst_path(new_subdir))) + shutil.copy(real_path, new_subdir) + else: + raise Exception("Unknown path: {path}".format(path=real_path)) + + +def is_ignored_path(path): + basename = os.path.basename(path) + if basename in IGNORE_DIRS: + print("[make_installer.py] Ignore: {dir}" + .format(dir=short_src_path(path))) + return True + for ext in IGNORE_EXT: + if path.endswith(ext): + print("[make_installer.py] Ignore: {file}" + .format(file=short_src_path(path))) + return True + return False + + +def delete_files_by_pattern(pattern): + assert len(pattern) > 2 + # Normalize unix slashes on Windows + pattern = pattern.replace("/", os.path.sep) + print("[make_installer.py] Delete: {pattern}" + .format(pattern=short_dst_path(pattern))) + files = glob.glob(pattern) + for f in files: + os.remove(f) + + +def create_empty_log_file(log_file): + # Normalize unix slashes on Windows + log_file = log_file.replace("/", os.path.sep) + print("[make_installer.py] Create: {file}" + .format(file=short_dst_path(log_file))) + with open(log_file, "wb") as fo: + fo.write("") + # On Linux and Mac chmod so that for cases when package is + # installed using sudo. When wheel package is created it + # will remember file permissions set. + if LINUX or MAC: + command = "chmod 666 {file}".format(file=log_file) + print("[make_installer.py] {command}" + .format(command=command.replace(SETUP_DIR, ""))) + subprocess.call(command, shell=True) + + +def short_src_path(path): + # Very long: \build\cef55_3.2883.1553.g80bd606_win32\ + find = os.path.basename(CEF_BINARIES_LIBRARIES) + if len(find) > 12: + path = path.replace(find, find[:12] + "*") + path = path.replace(ROOT_DIR, "") + return path + + +def short_dst_path(path): + path = path.replace(SETUP_DIR, "") + return path + + +if __name__ == "__main__": + main() diff --git a/tools/requirements.txt b/tools/requirements.txt index c5ba053d..abdc1828 100644 --- a/tools/requirements.txt +++ b/tools/requirements.txt @@ -1,2 +1,4 @@ -cython == 0.25.2 +Cython == 0.25.2 docopt >= 0.6.2 +setuptools +wheel diff --git a/unittests/_test_runner.py b/unittests/_test_runner.py index f25f48cb..8b970aca 100644 --- a/unittests/_test_runner.py +++ b/unittests/_test_runner.py @@ -3,9 +3,10 @@ """Run unit tests. With no arguments all tests are run. Read notes below. Usage: - _test_runner.py [FILE | _TESTCASE] + _test_runner.py [--debug] [FILE | _TESTCASE] Options: + --debug Enable debug info FILE Run tests from single file _TESTCASE Test cases matching pattern to run eg "file.TestCase". Calling with this argument is for internal use only. @@ -27,6 +28,9 @@ import re import subprocess +# Command line args +CUSTOM_CMDLINE_ARG = "" + def main(file_arg=""): # type: (str) -> None @@ -37,7 +41,12 @@ def main(file_arg=""): # Script arguments testcase_arg = "" - if len(sys.argv) > 1: + if len(sys.argv) > 1 and sys.argv[1].startswith("--"): + # Will allow to pass custom args like --debug to isolated tests + # (main_test.py for example). + global CUSTOM_CMDLINE_ARG + CUSTOM_CMDLINE_ARG = sys.argv[1] + elif len(sys.argv) > 1: if ".py" in sys.argv[1]: file_arg = sys.argv[1] else: @@ -141,7 +150,8 @@ def _run_suites_in_isolation(self, suites): # Run test using new instance of Python interpreter try: output = subprocess.check_output( - [sys.executable, "_test_runner.py", testcase_id], + [sys.executable, "_test_runner.py", testcase_id, + CUSTOM_CMDLINE_ARG], stderr=subprocess.STDOUT) exit_code = 0 except subprocess.CalledProcessError as exc: diff --git a/unittests/main_test.py b/unittests/main_test.py index a20af1da..05f34158 100644 --- a/unittests/main_test.py +++ b/unittests/main_test.py @@ -101,11 +101,15 @@ def test_main(self): print("Python {ver}".format(ver=sys.version[:6])) # Test initialization of CEF - cef.Initialize({ + settings = { "debug": False, "log_severity": cef.LOGSEVERITY_ERROR, "log_file": "", - }) + } + if "--debug" in sys.argv: + settings["debug"] = True + settings["log_severity"] = cef.LOGSEVERITY_INFO + cef.Initialize(settings) subtest_message("cef.Initialize() ok") # Test global handler