From 60ebfb8f9d3b5f0d299148594123f8d9e1234a04 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Feb 2017 17:23:10 +0100 Subject: [PATCH 1/8] Build v55 on Windows PART 2 (#294)... There are still issues with unittests and examples. Tkinter example launches fine, but crashes sometimes. Update vcproj files and other fixes. Add additional linker flag /LARGEADDRESSAWARE to Allow 32-bit processes to access 3GB of RAM. Set UNICODE for subprocess. Update build tools. Update module comments with great details on how these tools work. Refactor make-setup.py and make it work cross-platform (#299). Moved to tools/installer/make.py . Update docs. Update requirements.txt. Update tkinter example on Windows, doesn't support PNG images. --- api/ApplicationSettings.md | 2 + docs/Build-instructions.md | 3 + examples/resources/back.gif | Bin 0 -> 1145 bytes examples/resources/forward.gif | Bin 0 -> 1141 bytes examples/resources/reload.gif | Bin 0 -> 1177 bytes examples/resources/tkinter.gif | Bin 0 -> 5661 bytes examples/resources/tkinter.png | Bin 6265 -> 6294 bytes examples/tkinter_.py | 18 +- examples/wxpython.py | 1 + .../client_handler_py27_win32.vcproj | 4 +- src/client_handler/lifespan_handler.cpp | 2 + src/cpp_utils/cpp_utils_win32.vcproj | 1 + .../installer/build-n-run-wx-chromectrl.sh | 7 - src/mac/installer/.gitignore | 3 - src/mac/installer/__init__.py.template | 28 -- src/mac/installer/build_all.sh | 47 --- src/mac/installer/build_run_chromectrl.sh | 31 -- src/mac/installer/make-setup.py | 172 ---------- src/mac/installer/setup.py.template | 79 ----- ...proj => libcefpythonapp_py27_win32.vcproj} | 9 +- src/subprocess/main.cpp | 11 +- .../main_message_loop_external_pump_win.cpp | 9 +- src/subprocess/main_message_loop/util_win.cpp | 13 +- src/subprocess/main_message_loop/util_win.h | 3 +- ...s_32bit.vcproj => subprocess_win32.vcproj} | 9 +- src/window_info.pyx | 18 +- src/windows/compile.bat | 235 ------------- src/windows/installer/.gitignore | 3 - src/windows/installer/README.txt | 18 - src/windows/installer/__init__.py.template | 12 - src/windows/installer/build_all.bat | 213 ------------ src/windows/installer/innosetup.template | 165 ---------- src/windows/installer/make-installer.py | 93 ------ src/windows/installer/make-setup.py | 178 ---------- src/windows/installer/setup.cfg.template | 2 - src/windows/installer/setup.py.template | 68 ---- tools/automate.py | 301 +++++++++++------ tools/build.py | 306 ++++++++++------- tools/build_module.py | 19 +- tools/common.py | 95 ++++-- tools/installer/cefpython3.README.txt | 13 + tools/installer/cefpython3.__init__.py | 62 ++++ tools/installer/cefpython3.setup.py | 225 +++++++++++++ tools/make_installer.py | 309 ++++++++++++++++++ tools/requirements.txt | 4 +- unittests/_test_runner.py | 16 +- unittests/main_test.py | 8 +- 47 files changed, 1178 insertions(+), 1637 deletions(-) create mode 100644 examples/resources/back.gif create mode 100644 examples/resources/forward.gif create mode 100644 examples/resources/reload.gif create mode 100644 examples/resources/tkinter.gif delete mode 100644 src/linux/installer/build-n-run-wx-chromectrl.sh delete mode 100644 src/mac/installer/.gitignore delete mode 100644 src/mac/installer/__init__.py.template delete mode 100644 src/mac/installer/build_all.sh delete mode 100644 src/mac/installer/build_run_chromectrl.sh delete mode 100644 src/mac/installer/make-setup.py delete mode 100644 src/mac/installer/setup.py.template rename src/subprocess/{libcefpythonapp_py27_32bit.vcproj => libcefpythonapp_py27_win32.vcproj} (83%) rename src/subprocess/{subprocess_32bit.vcproj => subprocess_win32.vcproj} (88%) delete mode 100644 src/windows/compile.bat delete mode 100644 src/windows/installer/.gitignore delete mode 100644 src/windows/installer/README.txt delete mode 100644 src/windows/installer/__init__.py.template delete mode 100644 src/windows/installer/build_all.bat delete mode 100644 src/windows/installer/innosetup.template delete mode 100644 src/windows/installer/make-installer.py delete mode 100644 src/windows/installer/make-setup.py delete mode 100644 src/windows/installer/setup.cfg.template delete mode 100644 src/windows/installer/setup.py.template create mode 100644 tools/installer/cefpython3.README.txt create mode 100644 tools/installer/cefpython3.__init__.py create mode 100644 tools/installer/cefpython3.setup.py create mode 100644 tools/make_installer.py 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 0000000000000000000000000000000000000000..92362f5eecd32aac8d7bc190b3a8aad1f75215d1 GIT binary patch literal 1145 zcmZ?wbhEHb6k`x$_`a6GIQ5WO+5w}q<0k1xOwx~;q#rj*J7JW5(kT6uQTl14j8jG# zr;RgCn`E3a$po^`7-gQbEjVwKdBG_2qEQx*ykwMh*);p2N%m#aoJ%G-myNQo8s}U! z%DHZof6t=urcLn;`|`U+`47yCAK8^Zuq%IPR{7Pu>WgL7N6WfDR`q|a>;Kx+|8Xoj z>ri^ltLl2aP-Z#Q}1@1e!t_) zyPap=uQ~Q@%gGPB&wbc;;nU&EACF!8c(vKeuRZ*F z9f%$Q(YKqAzutQC?f#4J_g?*f@Z$U9*FT=T{_*t9&*yJ{K7ar3<@;anKLgS47a#t= z{`Bwdr$28$|NZ#o_s6flKYay~e?NZx_xbz3Z$JP4`u*?spMQV;{sV&l|Nk?LNTB$W zg^___9fJr^zJBsd;wX6N9HdJ&M+KFy-wfWpMagAOj7 zN{bjKUFj5A890?Km2u;P1ua6^B8i8-EPLkTGwH*T29JOycGE5e)2il`Q&*VxDsa{^ zAA0P-=g%VQ^krGl>ea!5ie4@n-ffNiPG>$UbG*D7Hv1pPr^D0E&z4|evAdAyetx#g z%5`D47B7E4*VuQ`f`VTTUT2-sgRjQKTxRPPwXyUuJgoly=IL#)$;m5a z*TSKgl;YCRC|RU)Q!cGiJNWkDaQ%}XOCK%`o$RbE4AuZQGX6mT literal 0 HcmV?d00001 diff --git a/examples/resources/forward.gif b/examples/resources/forward.gif new file mode 100644 index 0000000000000000000000000000000000000000..1032b9bee62df9f265de766f81bfdb252e9c5461 GIT binary patch literal 1141 zcmd^;|5K6&0LI@+oy^vB=ghfwXB%Cu^LFhOQ>GcBz4@g!&bGGevZ7Ogz*)M^jddy@ zC~C?Vca@sMn$9^jwK6@sCd4Aq48I`Sup?6;z}Snz3&P7g`aAaA=RWuR`uy-5&VUk# z)CeE~K>C2#JjMR}5inmxpeYHoM+BM*%zq5hMnReyq>X`eHAo+erH>KlY68RG$3e!# zf%8v6h6ZFzg6I660?$pyF((Pk={VLDfi(>>wXrNM$kKs@&l4`pCl${fELi{x4e`Z` z$t8y5k{A1ba>TROiENly>iwb&O)Nu`%DgE>Pe>PMGucyLmd--#d5En~|M^*BIhMr1 z4sfw#F7|DOj@|svQNHCU-+H{pmQ%k9RX9mJER~OvDll?|kL))eg9P66DsM)WH>(=U zu5o17I!@HXlp0r#7(OL|PfOq&vHes7LKPVct{TsZUr{Bi%qyniYcGnMjK$5SAN*W5 zU2HL*k-`NnNNyu`Nw&ytTjRE^aNAdS?JL!t%fdg6b=}LgT}Iv?HbIx|a<@r*%hWWm z+<4b`{hqO*-z>eodhM>c>7G^4wx<8;P~sIaoha~+l>*VO9^*9a(1Y&;fa4AXzUNC;lH(h z?2mQ!QJwwi0x~j-Dd(`sh4rcD4z0nVTlC|cf9ZI(>b9+I*w#F-!((@N zU?;lf^dfH5;l>cR581%n8>riZZg^0S*XQ$L{s8}*>~sfg0)T!X=FQ?ap8$e=Kpt*K zdaq2*>&Nd|I+LQ{1qS0zm?+%J?Fog77%@jqO2d8JvCmGZZW#Tle(+K`okGUNT_y21 z$8~CdrL-oYLV1xa;9bVP3sLG%kH<8n8OuXSB3}4m=?$nLZ)OFbDH_a;bVa8%r`ClE z`I+iHod+wo4hd68hAkhJ2E5;x6(6Sk?l<5Nm8-bf_0~7~@V(4Cz0~Z}^o}2+HQZl7 z>DH{z^y&P!ahr+jBk!4Javye^^%SbSTa3>zSx2S6*D=55yET!kh#VIURe4>l9}0Kf zW`D1bKC7^C!yw7--9<06S$arR8*DhHl3M1T>@{<9RI{9rlgEzClUv3jj`T(3eg0Qo zbqEl+eFXTVJ^6#!B6H2oEkO{E_s;$Efl&cH907-Z5>^0VxX?}8h%$N54*F7gYQW4P WyaotL(2zs6MT%Ml_^9wuAn`wRlK$lY literal 0 HcmV?d00001 diff --git a/examples/resources/reload.gif b/examples/resources/reload.gif new file mode 100644 index 0000000000000000000000000000000000000000..ef96a429162f6f778d74e4ee6cb47ab5db2061e8 GIT binary patch literal 1177 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 M07*qoM6N<$f{d~iSpWb4 literal 6265 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 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 From 97712b3a213b591b63c2f230f3af158f626b3b0d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 20 Feb 2017 21:11:31 +0100 Subject: [PATCH 2/8] Fix crashes in unit tests and examples on Windows with v55+ (#294)... Fixes to wxpython.py example, works great. The hello_world.py example also works good. Unit tests are working fine now. When not providing parent window handle (NULL) during browser creation, so that CEF creates top-level window itself, in such case must call SetAsPopup on Windows. Fixes to build.py tool to run all examples smoothly. --- examples/wxpython.py | 34 +++++++------ src/window_info.pyx | 2 +- tools/build.py | 66 +++++++++++++++----------- tools/installer/cefpython3.__init__.py | 3 +- tools/installer/cefpython3.setup.py | 3 +- tools/make_installer.py | 2 +- 6 files changed, 63 insertions(+), 47 deletions(-) diff --git a/examples/wxpython.py b/examples/wxpython.py index 2de7a752..8e7026fc 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -3,8 +3,11 @@ # To install wxPython on Linux type "sudo apt-get install python-wxtools". -# Tested with wxPython 2.8 on Linux, wxPython 3.0 on Windows/Mac -# and CEF Python v55.3+. +# Tested configurations: +# - wxPython 2.8 on Linux +# - wxPython 3.0.2.0 msw (classic) on Windows +# - wxPython 3.0 on Mac +# - CEF Python v55.3+ import wx from cefpython3 import cefpython as cef @@ -28,7 +31,7 @@ def main(): settings["auto_zooming"] = "system_dpi" # Embed DPI awareness xml manifest inside .exe (recommended, # most reliable) or call the SetProcessDpiAware function. - # noinspection PyUnresolvedReferences + # noinspection PyUnresolvedReferences, PyArgumentList cef.DpiAware.SetProcessDpiAware() cef.Initialize(settings=settings) app = CefApp(False) @@ -40,7 +43,7 @@ def main(): def check_versions(): print("[wxpython.py] CEF Python {ver}".format(ver=cef.__version__)) print("[wxpython.py] Python {ver}".format(ver=sys.version[:6])) - print("[wxpython.py] wx {ver}".format(ver=wx.version())) + print("[wxpython.py] wxPython {ver}".format(ver=wx.version())) # CEF Python version requirement assert cef.__version__ >= "55.3", "CEF Python v55.3+ required to run this" @@ -75,11 +78,10 @@ def setup_icon(self): def create_menu(self): filemenu = wx.Menu() - filemenu.Append(1, "Open") - exit_ = filemenu.Append(2, "Exit") - self.Bind(wx.EVT_MENU, self.OnClose, exit_) + filemenu.Append(1, "Some option") + exit_ = filemenu.Append(2, "Another option") aboutmenu = wx.Menu() - aboutmenu.Append(1, "CEF Python") + aboutmenu.Append(1, "Yet another option") menubar = wx.MenuBar() menubar.Append(filemenu, "&File") menubar.Append(aboutmenu, "&About") @@ -93,11 +95,12 @@ def embed_browser(self): self.browser.SetClientHandler(FocusHandler()) def OnSetFocus(self, _): - if not self.brower: + if not self.browser: return if WINDOWS: # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSetFocus(self.GetHandleForBrowser(), 0, 0, 0) + cef.WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), + 0, 0, 0) self.browser.SetFocus(True) def OnSize(self, _): @@ -105,11 +108,11 @@ def OnSize(self, _): return if WINDOWS: # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(self.GetHandleForBrowser(), 0, 0, 0) + cef.WindowUtils.OnSize(self.browser_panel.GetHandle(), + 0, 0, 0) elif LINUX: (x, y) = (0, 0) (width, height) = self.browser_panel.GetSizeTuple() - # noinspection PyUnresolvedReferences self.browser.SetBounds(x, y, width, height) self.browser.NotifyMoveOrResizeStarted() @@ -154,9 +157,10 @@ def OnGotFocus(self, browser, **_): # window (alt+tab) and then back to this example, keyboard # focus becomes broken, you can't type anything, even # though a type cursor blinks in web view. - print("[wxpython.py] FocusHandler.OnGotFocus:" - " keyboard focus fix (#284)") - browser.SetFocus(True) + if LINUX: + print("[wxpython.py] FocusHandler.OnGotFocus:" + " keyboard focus fix (#284)") + browser.SetFocus(True) class CefApp(wx.App): diff --git a/src/window_info.pyx b/src/window_info.pyx index 988c665e..27c6cf16 100644 --- a/src/window_info.pyx +++ b/src/window_info.pyx @@ -87,7 +87,7 @@ cdef class WindowInfo: # On Windows when parent window handle is 0 then SetAsPopup() # must be called instead. if parentWindowHandle == 0: - self.SetAsPopup(parentWindowHandle, "Popup") + self.SetAsPopup(parentWindowHandle, "") return if parentWindowHandle != 0\ and not WindowUtils.IsWindowHandle(parentWindowHandle): diff --git a/tools/build.py b/tools/build.py index 6c424a78..52551837 100644 --- a/tools/build.py +++ b/tools/build.py @@ -648,16 +648,22 @@ def install_and_run(): # Make setup installer print("[build.py] Make setup installer") 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)) + ret = os.system("{python} {make_tool} --version {version}" + .format(python=sys.executable, + make_tool=make_tool, + version=VERSION)) + if ret != 0: + print("[build.py] ERROR while making installer package") + sys.exit(ret) # Install print("[build.py] Install the cefpython package") os.chdir(setup_installer_dir) - os.system("{sudo} {python} setup.py install" - .format(sudo=get_sudo(), python=sys.executable)) + ret = os.system("{sudo} {python} setup.py install" + .format(sudo=get_sudo(), python=sys.executable)) + if ret != 0: + print("[build.py] ERROR while installing package") + sys.exit(ret) os.chdir(BUILD_DIR) # Run unittests @@ -666,32 +672,36 @@ def install_and_run(): ret = os.system("{python} {test_runner}" .format(python=sys.executable, test_runner=test_runner)) if ret != 0: + print("[build.py] ERROR while running unit tests") sys.exit(ret) # Run examples print("[build.py] Run examples") - if KIVY_FLAG: - run_examples = "{python} {linux_dir}/deprecated_64bit/kivy_.py" - else: - run_examples = ("cd {examples_dir}" - " && {python} hello_world.py" - " && {python} wxpython.py" - " && {python} gtk2.py" - " && {python} gtk2.py --message-loop-timer" - # " && {python} gtk3.py" - " && {python} tkinter_.py" - " && {python} qt.py pyqt" - " && {python} qt.py pyside") - if LINUX: - run_examples += (" && {python}" - " {linux_dir}/deprecated_64bit/kivy_.py") - run_examples.format( - python=sys.executable, - linux_dir=LINUX_DIR, - examples_dir=EXAMPLES_DIR) - os.system(run_examples) - - print("[build.py] DONE") + os.chdir(EXAMPLES_DIR) + examples = list() + if not KIVY_FLAG: + examples.extend([ + "hello_world.py", + "wxpython.py", + "gtk2.py", + "gtk2.py --message-loop-timer", + "gtk3.py", + "tkinter_.py", + "qt.py pyqt", + "qt.py pyside", + ]) + if LINUX: + examples.append("{linux_dir}/deprecated_64bit/kivy_.py" + .format(linux_dir=LINUX_DIR)) + for example in examples: + ret = os.system("{python} {example}" + .format(python=sys.executable, example=example)) + if ret != 0: + print("[build.py] ERROR while running example: {example}" + .format(example=example)) + sys.exit(1) + + print("[build.py] Everything OK") def get_sudo(): diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index ca54b44b..5e794a0a 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -2,7 +2,8 @@ # Licensed under BSD 3-clause license. # NOTE: Template variables like {{VERSION}} are replaced with actual -# values when make.py tool generates this package installer. +# values when make_installer.py tool generates this package +# installer. import os import sys diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 646c5fb3..292b2ee3 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -16,7 +16,8 @@ """ # NOTE: Template variables like {{VERSION}} are replaced with actual -# values when make.py tool generates this package installer. +# values when make_installer.py tool generates this package +# installer. import copy import os diff --git a/tools/make_installer.py b/tools/make_installer.py index 87f1a12d..5d4f7501 100644 --- a/tools/make_installer.py +++ b/tools/make_installer.py @@ -5,7 +5,7 @@ Create setup.py package installer. Usage: - make.py VERSION + make_installer.py VERSION Options: VERSION Version number eg. 50.0 From 9bd3accd5eee4411dd8b74bf5ff7b1520bf4b4b7 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Feb 2017 15:21:55 +0100 Subject: [PATCH 3/8] Fix issues with examples on Windows with v55+ (#294)... All examples do run fine on Windows now. Add run_examples.py tool to run all examples that can be run on current configuration. --- examples/gtk2.py | 60 ++++++++++----- examples/gtk3.py | 59 +++++++++++++-- examples/hello_world.py | 2 +- examples/qt.py | 104 ++++++++++++++++++-------- examples/tkinter_.py | 21 ++++-- examples/wxpython.py | 27 ++++--- src/window_utils_linux.pyx | 19 +++++ src/window_utils_mac.pyx | 19 +++++ src/window_utils_win.pyx | 14 ++-- tools/build.py | 28 ++----- tools/common.py | 2 +- tools/run_examples.py | 147 +++++++++++++++++++++++++++++++++++++ 12 files changed, 396 insertions(+), 106 deletions(-) create mode 100644 tools/run_examples.py diff --git a/examples/gtk2.py b/examples/gtk2.py index 092c82c8..c7b4c84b 100644 --- a/examples/gtk2.py +++ b/examples/gtk2.py @@ -1,5 +1,5 @@ # Example of embedding CEF Python browser using PyGTK library (GTK 2). -# Tested with GTK 2.24 and CEF Python v55.3+, only on Linux. +# Tested with GTK 2.24 and CEF Python v55.3+, on Windows/Linux. # Known issue on Linux: Keyboard focus problem (Issue #284). from cefpython3 import cefpython as cef @@ -8,16 +8,25 @@ import gobject import sys import os +import platform + +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms +WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") # In CEF you can run message loop in two ways (see API docs for more details): -# 1. By calling cef.MessageLoop() instead of an application-provided +# 1. By calling cef.MessageLoopWork() in a timer - each call performs +# a single iteration of CEF message loop processing. +# 2. By calling cef.MessageLoop() instead of an application-provided # message loop to get the best balance between performance and CPU # usage. This function will block until a quit message is received by -# the system. -# 2. By calling cef.MessageLoopWork() in a timer - each call performs -# a single iteration of CEF message loop processing. -MESSAGE_LOOP_BEST = 1 -MESSAGE_LOOP_TIMER = 2 # Pass --message-loop-timer flag to script to use this +# the system. This seem to work only on Linux in GTK example. +MESSAGE_LOOP_TIMER = 1 +MESSAGE_LOOP_CEF = 2 # Pass --message-loop-cef flag to script on Linux g_message_loop = None @@ -28,7 +37,7 @@ def main(): cef.Initialize() gobject.threads_init() Gtk2Example() - if g_message_loop == MESSAGE_LOOP_BEST: + if g_message_loop == MESSAGE_LOOP_CEF: cef.MessageLoop() else: gtk.main() @@ -46,13 +55,13 @@ def check_versions(): def configure_message_loop(): global g_message_loop - if "--message-loop-timer" in sys.argv: + if "--message-loop-cef" in sys.argv: + print("[gkt2.py] Message loop mode: CEF (best performance)") + g_message_loop = MESSAGE_LOOP_CEF + sys.argv.remove("--message-loop-cef") + else: print("[gkt2.py] Message loop mode: TIMER") g_message_loop = MESSAGE_LOOP_TIMER - sys.argv.remove("--message-loop-timer") - else: - print("[gkt2.py] Message loop mode: BEST") - g_message_loop = MESSAGE_LOOP_BEST if len(sys.argv) > 1: print("[gkt2.py] ERROR: unknown argument passed") sys.exit(1) @@ -83,7 +92,7 @@ def __init__(self): self.main_window.add(self.vbox) windowInfo = cef.WindowInfo() - windowInfo.SetAsChild(self.main_window.window.xid) + windowInfo.SetAsChild(self.get_handle()) self.browser = cef.CreateBrowserSync(windowInfo, settings={}, url="https://www.google.com/") self.browser.SetClientHandler(LoadHandler()) @@ -95,6 +104,12 @@ def __init__(self): if g_message_loop == MESSAGE_LOOP_TIMER: gobject.timeout_add(10, self.on_timer) + def get_handle(self): + if LINUX: + return self.main_window.window.xid + else: + return self.main_window.window.handle + def create_menu(self): item1 = gtk.MenuItem('MenuBar') item1.show() @@ -131,16 +146,22 @@ def on_vbox_size_allocate(self, _, data): y = data.y + self.menubar_height width = data.width height = data.height - self.menubar_height - self.browser.SetBounds(x, y, width, height) + if WINDOWS: + WindowUtils.OnSize(self.get_handle(), 0, 0, 0) + elif LINUX: + self.browser.SetBounds(x, y, width, height) def on_menubar_size_allocate(self, _, data): self.menubar_height = data.height def on_exit(self, *_): + if self.exiting: + print("[gtk2.py] on_exit() called, but already exiting") + return self.exiting = True self.browser.CloseBrowser(True) self.browser = None - if g_message_loop == MESSAGE_LOOP_BEST: + if g_message_loop == MESSAGE_LOOP_CEF: cef.QuitMessageLoop() else: gtk.main_quit() @@ -158,9 +179,10 @@ def OnLoadStart(self, browser, **_): # sometimes during initial loading, keyboard focus may # break and it is not possible to type anything, even # though a type cursor blinks in web view. - print("[gtk2.py] LoadHandler.OnLoadStart:" - " keyboard focus fix (#284)") - browser.SetFocus(True) + if LINUX: + print("[gtk2.py] LoadHandler.OnLoadStart:" + " keyboard focus fix (#284)") + browser.SetFocus(True) self.initial_app_loading = False diff --git a/examples/gtk3.py b/examples/gtk3.py index 5820dcd7..2223362b 100644 --- a/examples/gtk3.py +++ b/examples/gtk3.py @@ -1,12 +1,33 @@ -# ! CURRENTLY BROKEN ! with v54+ (Issue #261). -# Example of embedding CEF Python browser using PyGObject library (GTK 3). -# Tested with GTK 3.10 and CEF Python v53.1+, only on Linux. +# Example of embedding CEF Python browser using PyGObject/PyGI (GTK 3). + +# Linux note: This example is currently broken in v54+ on Linux (Issue #261). +# It works fine with cefpython v53. + +# Tested configurations: +# - GTK 3.18 on Windows +# - GTK 3.10 on Linux +# - CEF Python v53.1+ from cefpython3 import cefpython as cef +import ctypes # noinspection PyUnresolvedReferences -from gi.repository import GdkX11, Gtk, GObject, GdkPixbuf +from gi.repository import Gtk, GObject, Gdk, GdkPixbuf import sys import os +import platform + +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms +WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + +# Linux imports +if LINUX: + # noinspection PyUnresolvedReferences + from gi.repository import GdkX11 def main(): @@ -25,9 +46,10 @@ def main(): class Gtk3Example(Gtk.Application): def __init__(self): - super(Gtk3Example, self).__init__(application_id='cefpython.gtk') + super(Gtk3Example, self).__init__(application_id='cefpython.gtk3') self.browser = None self.window = None + self.win32_handle = None def run(self, argv): GObject.threads_init() @@ -36,6 +58,22 @@ def run(self, argv): self.connect("shutdown", self.on_shutdown) return super(Gtk3Example, self).run(argv) + def get_handle(self): + if LINUX: + return self.window.get_property("window").get_xid() + elif WINDOWS: + Gdk.threads_enter() + ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p + ctypes.pythonapi.PyCapsule_GetPointer.argtypes = \ + [ctypes.py_object] + gpointer = ctypes.pythonapi.PyCapsule_GetPointer( + self.window.get_property("window").__gpointer__, None) + gdk_dll = ctypes.CDLL("libgdk-3-0.dll") + self.win32_handle = gdk_dll.gdk_win32_window_get_handle( + gpointer) + Gdk.threads_leave() + return self.win32_handle + def on_timer(self): cef.MessageLoopWork() return True @@ -51,10 +89,13 @@ def on_activate(self, *_): self.setup_icon() self.window.realize() window_info = cef.WindowInfo() - window_info.SetAsChild(self.window.get_property("window").get_xid()) + window_info.SetAsChild(self.get_handle()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") self.window.show_all() + # Must set size of the window again after it was shown, + # otherwise browser occupies only part of the window area. + self.window.resize(*self.window.get_default_size()) def on_configure(self, *_): if self.browser: @@ -63,7 +104,11 @@ def on_configure(self, *_): def on_size_allocate(self, _, data): if self.browser: - self.browser.SetBounds(data.x, data.y, data.width, data.height) + if WINDOWS: + WindowUtils.OnSize(self.win32_handle, 0, 0, 0) + elif LINUX: + self.browser.SetBounds(data.x, data.y, + data.width, data.height) def on_focus_in(self, *_): if self.browser: diff --git a/examples/hello_world.py b/examples/hello_world.py index 5417bcfd..0c64ab84 100644 --- a/examples/hello_world.py +++ b/examples/hello_world.py @@ -1,5 +1,5 @@ # Hello world example. Doesn't depend on any third party GUI framework. -# Tested with CEF Python v55.3+, only on Linux. +# Tested with CEF Python v55.3+. from cefpython3 import cefpython as cef import sys diff --git a/examples/qt.py b/examples/qt.py index 1877272f..995f637f 100644 --- a/examples/qt.py +++ b/examples/qt.py @@ -1,21 +1,30 @@ # Example of embedding CEF Python browser using PyQt/PySide libraries. # This example has two widgets: a navigation bar and a browser. # -# Tested with PyQt 4.10.4 (4.8.6), PySide 1.2.1 (4.8.6) -# and CEF Python v55.3+, only on Linux. +# Tested configurations: +# - PyQt 4.11.4 (4.8.7) on Windows +# - PySide 1.2.4 (4.8.7) on Windows +# - PyQt 4.10.4 (4.8.6) on Linux +# - PySide 1.2.1 (4.8.6) on Linux +# - CEF Python v55.4+ +from cefpython3 import cefpython as cef +import ctypes import os -import sys import platform -from cefpython3 import cefpython as cef +import sys # PyQt imports if "pyqt" in sys.argv: + # noinspection PyUnresolvedReferences from PyQt4.QtGui import * + # noinspection PyUnresolvedReferences from PyQt4.QtCore import * # PySide imports elif "pyside" in sys.argv: + # noinspection PyUnresolvedReferences import PySide + # noinspection PyUnresolvedReferences from PySide import QtCore # noinspection PyUnresolvedReferences from PySide.QtGui import * @@ -27,9 +36,15 @@ print(" qt.py pyside") sys.exit(1) -# Constants -LINUX = (platform.system() == "Linux") +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + +# Configuration WIDTH = 800 HEIGHT = 600 @@ -59,6 +74,7 @@ def check_versions(): print("[qt.py] Python {ver}".format(ver=sys.version[:6])) # PyQt version if "pyqt" in sys.argv: + # noinspection PyUnresolvedReferences print("[qt.py] PyQt {v1} ({v2})".format( v1=PYQT_VERSION_STR, v2=qVersion())) # PySide version @@ -91,6 +107,8 @@ def setupLayout(self): layout.addWidget(self.cef_widget, 1, 0) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) + layout.setRowStretch(0, 0) + layout.setRowStretch(1, 1) frame = QFrame() frame.setLayout(layout) self.setCentralWidget(frame) @@ -98,20 +116,20 @@ def setupLayout(self): self.cef_widget.embedBrowser() def focusInEvent(self, event): - # This event seems to never get called, as CEF is stealing all - # focus due to Issue #284. - if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSetFocus(int(self.centralWidget().winId()), - 0, 0, 0) - print("[qt.py] focusInEvent") + # This event seems to never get called on Linux, as CEF is + # stealing all focus due to Issue #284. + # print("[qt.py] focusInEvent") if self.cef_widget.browser: + if WINDOWS: + WindowUtils.OnSetFocus(self.cef_widget.getHandle(), + 0, 0, 0) self.cef_widget.browser.SetFocus(True) def focusOutEvent(self, event): - # This event seems to never get called, as CEF is stealing all - # focus due to Issue #284. - print("[qt.py] focusOutEvent") + # This event seems to never get called on Linux, as CEF is + # stealing all focus due to Issue #284. + # print("[qt.py] focusOutEvent") + pass def closeEvent(self, event): # Close browser (force=True) and free CEF reference @@ -212,12 +230,34 @@ def embedBrowser(self): self.width = 0 self.height = 0 window_info = cef.WindowInfo() - window_info.SetAsChild(int(self.winId())) + window_info.SetAsChild(self.getHandle()) self.browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/") self.browser.SetClientHandler(LoadHandler(self.parent.navigation_bar)) self.browser.SetClientHandler(FocusHandler()) + def getHandle(self): + # PySide bug: QWidget.winId() returns + # There is no easy way to convert it to int. + try: + return int(self.winId()) + except: + if sys.version_info[0] == 2: + # Python 2 + ctypes.pythonapi.PyCObject_AsVoidPtr.restype = ( + ctypes.c_void_p) + ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = ( + [ctypes.py_object]) + return ctypes.pythonapi.PyCObject_AsVoidPtr(self.winId()) + else: + # Python 3 + ctypes.pythonapi.PyCapsule_GetPointer.restype = ( + ctypes.c_void_p) + ctypes.pythonapi.PyCapsule_GetPointer.argtypes = ( + [ctypes.py_object]) + return ctypes.pythonapi.PyCapsule_GetPointer( + self.winId(), None) + def moveEvent(self, _): # pos = event.pos() # self.x = pos.x() @@ -226,11 +266,11 @@ def moveEvent(self, _): self.y = 0 if self.browser: if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) + WindowUtils.OnSize(self.getHandle(), 0, 0, 0) elif LINUX: - # noinspection PyUnresolvedReferences - self.browser.SetBounds(self.x, self.y, self.width, self.height) + self.browser.SetBounds(self.x, self.y, + self.width, self.height) + self.browser.NotifyMoveOrResizeStarted() def resizeEvent(self, event): size = event.size() @@ -238,11 +278,11 @@ def resizeEvent(self, event): self.height = size.height() if self.browser: if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(int(self.winId()), 0, 0, 0) + WindowUtils.OnSize(self.getHandle(), 0, 0, 0) elif LINUX: - # noinspection PyUnresolvedReferences - self.browser.SetBounds(self.x, self.y, self.width, self.height) + self.browser.SetBounds(self.x, self.y, + self.width, self.height) + self.browser.NotifyMoveOrResizeStarted() class CefApplication(QApplication): @@ -298,9 +338,10 @@ def OnLoadStart(self, browser, **_): # sometimes during initial loading, keyboard focus may # break and it is not possible to type anything, even # though a type cursor blinks in web view. - print("[qt.py] LoadHandler.OnLoadStart:" - " keyboard focus fix no. 2 (#284)") - browser.SetFocus(True) + if LINUX: + print("[qt.py] LoadHandler.OnLoadStart:" + " keyboard focus fix no. 2 (#284)") + browser.SetFocus(True) self.initial_app_loading = False @@ -328,9 +369,10 @@ def OnGotFocus(self, browser, **_): # window (alt+tab) and then back to this example, keyboard # focus becomes broken, you can't type anything, even # though a type cursor blinks in web view. - print("[qt.py] FocusHandler.OnGotFocus:" - " keyboard focus fix no. 1 (#284)") - browser.SetFocus(True) + if LINUX: + print("[qt.py] FocusHandler.OnGotFocus:" + " keyboard focus fix no. 1 (#284)") + browser.SetFocus(True) if __name__ == '__main__': diff --git a/examples/tkinter_.py b/examples/tkinter_.py index c8385992..dc5abf24 100644 --- a/examples/tkinter_.py +++ b/examples/tkinter_.py @@ -1,7 +1,9 @@ # Example of embedding CEF Python browser using Tkinter toolkit. # This example has two widgets: a navigation bar and a browser. # -# Tested with Tk 8.6 and CEF Python v55.3+, only on Linux. +# Tested configurations: +# - Tk 8.6 and CEF Python v55.3+ on Linux +# - Tk 8.5 and CEF Python v55.4+ on Windows # # Known issue on Linux: When typing url, mouse must be over url # entry widget otherwise keyboard focus is lost (Issue #255 @@ -17,10 +19,18 @@ import platform import logging as _logging +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms +WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + # 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" +IMAGE_EXT = ".gif" if WINDOWS else ".png" def main(): @@ -278,10 +288,9 @@ def on_root_configure(self): def on_mainframe_configure(self, width, height): if self.browser: - if platform.system() == "Windows": - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(self.winfo_id(), 0, 0, 0) - elif platform.system() == "Linux": + if WINDOWS: + WindowUtils.OnSize(self.winfo_id(), 0, 0, 0) + elif LINUX: self.browser.SetBounds(0, 0, width, height) self.browser.NotifyMoveOrResizeStarted() diff --git a/examples/wxpython.py b/examples/wxpython.py index 8e7026fc..226642c2 100644 --- a/examples/wxpython.py +++ b/examples/wxpython.py @@ -5,9 +5,8 @@ # Tested configurations: # - wxPython 2.8 on Linux -# - wxPython 3.0.2.0 msw (classic) on Windows -# - wxPython 3.0 on Mac -# - CEF Python v55.3+ +# - wxPython 3.0 on Windows +# - CEF Python v55.4+ import wx from cefpython3 import cefpython as cef @@ -15,9 +14,15 @@ import sys import os -# Constants -LINUX = (platform.system() == "Linux") +# Fix for PyCharm hints warnings +WindowUtils = cef.WindowUtils() + +# Platforms WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") +MAC = (platform.system() == "Darwin") + +# Configuration WIDTH = 800 HEIGHT = 600 @@ -79,7 +84,7 @@ def setup_icon(self): def create_menu(self): filemenu = wx.Menu() filemenu.Append(1, "Some option") - exit_ = filemenu.Append(2, "Another option") + filemenu.Append(2, "Another option") aboutmenu = wx.Menu() aboutmenu.Append(1, "Yet another option") menubar = wx.MenuBar() @@ -98,18 +103,16 @@ def OnSetFocus(self, _): if not self.browser: return if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), - 0, 0, 0) + WindowUtils.OnSetFocus(self.browser_panel.GetHandle(), + 0, 0, 0) self.browser.SetFocus(True) def OnSize(self, _): if not self.browser: return if WINDOWS: - # noinspection PyUnresolvedReferences - cef.WindowUtils.OnSize(self.browser_panel.GetHandle(), - 0, 0, 0) + WindowUtils.OnSize(self.browser_panel.GetHandle(), + 0, 0, 0) elif LINUX: (x, y) = (0, 0) (width, height) = self.browser_panel.GetSizeTuple() diff --git a/src/window_utils_linux.pyx b/src/window_utils_linux.pyx index 885f82f8..da4030cc 100644 --- a/src/window_utils_linux.pyx +++ b/src/window_utils_linux.pyx @@ -11,6 +11,25 @@ class WindowUtils: # You have to overwrite this class and provide implementations # for these methods. + @staticmethod + def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnSize(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnEraseBackground(long windowHandle, long msg, long wparam, + long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + @staticmethod def GetParentHandle(WindowHandle windowHandle): Debug("WindowUtils::GetParentHandle() not implemented (returns 0)") diff --git a/src/window_utils_mac.pyx b/src/window_utils_mac.pyx index 742a9b8f..08eb7b85 100644 --- a/src/window_utils_mac.pyx +++ b/src/window_utils_mac.pyx @@ -8,6 +8,25 @@ class WindowUtils: # You have to overwrite this class and provide implementations # for these methods. + @staticmethod + def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnSize(long windowHandle, long msg, long wparam, long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + + @staticmethod + def OnEraseBackground(long windowHandle, long msg, long wparam, + long lparam): + # Available only on Windows, but have it available on other + # platforms so that PyCharm doesn't warn about unresolved reference. + pass + @staticmethod def GetParentHandle(WindowHandle windowHandle): Debug("WindowUtils::GetParentHandle() not implemented (returns 0)") diff --git a/src/window_utils_win.pyx b/src/window_utils_win.pyx index 81d80143..c40bd383 100644 --- a/src/window_utils_win.pyx +++ b/src/window_utils_win.pyx @@ -4,10 +4,10 @@ include "cefpython.pyx" -class WindowUtils: +class WindowUtils(object): @staticmethod - def OnSetFocus(int windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return 0 @@ -15,7 +15,7 @@ class WindowUtils: return 0 @staticmethod - def OnSize(int windowHandle, long msg, long wparam, long lparam): + def OnSize(long windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return DefWindowProc(windowHandle, msg, wparam, lparam) @@ -35,7 +35,7 @@ class WindowUtils: return DefWindowProc(windowHandle, msg, wparam, lparam) @staticmethod - def OnEraseBackground(int windowHandle, long msg, long wparam, + def OnEraseBackground(long windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: @@ -134,11 +134,11 @@ class WindowUtils: ICON_SMALL, parentIconSmall) @staticmethod - def GetParentHandle(int windowHandle): - return GetParent(windowHandle) + def GetParentHandle(long windowHandle): + return GetParent(windowHandle) @staticmethod - def IsWindowHandle(int windowHandle): + def IsWindowHandle(long windowHandle): IF UNAME_SYSNAME == "Windows": return bool(IsWindow(windowHandle)) ELSE: diff --git a/tools/build.py b/tools/build.py index 52551837..c7d742ec 100644 --- a/tools/build.py +++ b/tools/build.py @@ -678,28 +678,12 @@ def install_and_run(): # Run examples print("[build.py] Run examples") os.chdir(EXAMPLES_DIR) - examples = list() - if not KIVY_FLAG: - examples.extend([ - "hello_world.py", - "wxpython.py", - "gtk2.py", - "gtk2.py --message-loop-timer", - "gtk3.py", - "tkinter_.py", - "qt.py pyqt", - "qt.py pyside", - ]) - if LINUX: - examples.append("{linux_dir}/deprecated_64bit/kivy_.py" - .format(linux_dir=LINUX_DIR)) - for example in examples: - ret = os.system("{python} {example}" - .format(python=sys.executable, example=example)) - if ret != 0: - print("[build.py] ERROR while running example: {example}" - .format(example=example)) - sys.exit(1) + run_examples = os.path.join(TOOLS_DIR, "run_examples.py") + ret = os.system("{python} {run_examples}" + .format(python=sys.executable, run_examples=run_examples)) + if ret != 0: + print("[build.py] ERROR while running examples") + sys.exit(1) print("[build.py] Everything OK") diff --git a/tools/common.py b/tools/common.py index fb0a733b..9a5c5b61 100644 --- a/tools/common.py +++ b/tools/common.py @@ -22,8 +22,8 @@ OS_POSTFIX2 = "linux32" if ARCH32 else "linux64" # Platforms -LINUX = (platform.system() == "Linux") WINDOWS = (platform.system() == "Windows") +LINUX = (platform.system() == "Linux") MAC = (platform.system() == "Darwin") # Python version eg. 27 diff --git a/tools/run_examples.py b/tools/run_examples.py new file mode 100644 index 00000000..a89084e7 --- /dev/null +++ b/tools/run_examples.py @@ -0,0 +1,147 @@ +""" +Run all examples that can be run on current configuration. + +Note on GTK 2 / GTK 3 on Windows: + Installing both PyGTK and PyGI on Windows will cause errors. + You can install only one of these packages. +""" + +from common import * + +import importlib +import os +import sys + + +def main(): + os.chdir(EXAMPLES_DIR) + + packages = check_installed_packages() + examples = list() + examples.append("hello_world.py") + succeeded = list() + failed = list() + passed = list() + + # wxpython + if packages["wx"]: + examples.append("wxpython.py") + else: + print("[run_examples.py] PASS: wxpython.py (wxPython not installed)") + passed.append("wxpython.py") + + # gtk2 + if packages["gtk"]: + examples.append("gtk2.py") + if LINUX: + examples.append("gtk2.py --message-loop-cef") + else: + print("[run_examples.py] PASS: gtk2.py (Gtk 2 not installed") + passed.append("gtk2.py") + + # gtk3 + if packages["gi"]: + if not LINUX: + examples.append("gtk3.py") + else: + # Gtk 3 example is currently broken on Linux (Issue #261) + print("[run_examples.py] PASS: gtk3.py (Issue #261)") + passed.append("gtk3.py") + else: + print("[run_examples.py] PASS: gtk3.py (Gtk 3 not installed)") + passed.append("gtk3.py") + + # pyqt + if packages["PyQt4"]: + examples.append("qt.py pyqt") + else: + print("[run_examples.py] PASS: qt.py pyqt (PyQt4 not installed)") + passed.append("qt.py pyqt") + + # pyside + if packages["PySide"]: + examples.append("qt.py pyside") + else: + print("[run_examples.py] PASS: qt.py pyside (PySide not installed)") + passed.append("qt.py pyside") + + # tkinter + if packages["tkinter"] or packages["Tkinter"]: + examples.append("tkinter_.py") + else: + print(["run_examples.py] PASS: tkinter_.py (tkinter not installed)"]) + passed.append("tkinter_.py") + + # kivy + if LINUX and packages["kivy"] and packages["gtk"]: + if "--kivy" in sys.argv: + # When --kivy flag passed run only Kivy example + examples = list() + passed = list() + examples.append("{linux_dir}/binaries_64bit/kivy_.py" + .format(linux_dir=LINUX_DIR)) + + # Run all + for example in examples: + print("[run_examples.py] Running '{example}'..." + .format(example=example)) + ret = os.system("{python} {example}" + .format(python=sys.executable, example=example)) + if ret == 0: + succeeded.append(example) + else: + print("[run_examples.py] ERROR while running example: {example}" + .format(example=example)) + failed.append(example) + + # Summary + summary = "" + for example in succeeded: + summary += " OK {example}{nl}"\ + .format(example=example, nl=os.linesep) + for example in failed: + summary += " ERROR {example}{nl}"\ + .format(example=example, nl=os.linesep) + for example in passed: + summary += " PASS {example}{nl}"\ + .format(example=example, nl=os.linesep) + summary = summary[:-(len(os.linesep))] + print("[run_examples.py] SUMMARY:") + print(summary.format()) + + # OK or error message + passed_msg = "" + if passed: + passed_msg = ". Passed: {passed}.".format(passed=len(passed)) + if len(failed): + print("[run_examples.py] ERRORS({failed}) while running examples" + "{passed_msg}" + .format(failed=len(failed), passed_msg=passed_msg)) + sys.exit(1) + else: + print("[run_examples.py] OK({succeeded}){passed_msg}" + .format(succeeded=len(succeeded), passed_msg=passed_msg)) + + +def check_installed_packages(): + packages = { + "gtk": False, + "gi": False, + "kivy": False, + "PyQt4": False, + "PySide": False, + "tkinter": False, + "Tkinter": False, + "wx": False, + } + for package in packages: + try: + importlib.import_module(package) + packages[package] = True + except ImportError: + packages[package] = False + return packages + + +if __name__ == "__main__": + main() From 6f7c52da12704313009b834e086f0cd102b3d450 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Tue, 21 Feb 2017 16:59:14 +0100 Subject: [PATCH 4/8] Update installer to include platform tag when generating .whl file. --- tools/build.py | 7 +++++-- tools/installer/cefpython3.setup.py | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/tools/build.py b/tools/build.py index c7d742ec..0c2a84a4 100644 --- a/tools/build.py +++ b/tools/build.py @@ -678,9 +678,12 @@ def install_and_run(): # Run examples print("[build.py] Run examples") os.chdir(EXAMPLES_DIR) + kivy_flag = "--kivy" if KIVY_FLAG else "" run_examples = os.path.join(TOOLS_DIR, "run_examples.py") - ret = os.system("{python} {run_examples}" - .format(python=sys.executable, run_examples=run_examples)) + ret = os.system("{python} {run_examples} {kivy_flag}" + .format(python=sys.executable, + run_examples=run_examples, + kivy_flag=kivy_flag)) if ret != 0: print("[build.py] ERROR while running examples") sys.exit(1) diff --git a/tools/installer/cefpython3.setup.py b/tools/installer/cefpython3.setup.py index 292b2ee3..bfdc570d 100644 --- a/tools/installer/cefpython3.setup.py +++ b/tools/installer/cefpython3.setup.py @@ -24,6 +24,7 @@ import platform import subprocess import sys +import sysconfig # The setuptools package is not installed by default on a clean # Ubuntu. Might be also a case on Windows. Also Python Eggs @@ -92,6 +93,19 @@ def get_tag(self): return tag cmdclass["bdist_wheel"] = custom_bdist_wheel +elif platform.system() in ["Windows", "Linux"] and "bdist_wheel" in sys.argv: + # On Windows and Linux platform tag is always "any". + print("[setup.py] Overload bdist_wheel command to fix platform tag") + 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 = sysconfig.get_platform() + tag = (tag[0], tag[1], platform_tag) + return tag + + cmdclass["bdist_wheel"] = custom_bdist_wheel def main(): @@ -132,7 +146,12 @@ def main(): "Topic :: Software Development :: User Interfaces", ], ) - print("[setup.py] OK installed") + if "install" in sys.argv: + print("[setup.py] OK installed") + elif "bdist_wheel" in sys.argv: + print("[setup.py] OK created wheel package in dist/ directory") + else: + print("[setup.py] Unknown command line arguments") def get_package_data(): From 5ce5c368abaf973d0c65fad4d7f136c18cb06279 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Wed, 22 Feb 2017 12:36:24 +0100 Subject: [PATCH 5/8] Fix DevTools window not showing on Windows (#303)... Add "Quick build instructions for Windows" section indocs/Build-instructions. Fix invalid types for window handles on Windows 64-bit (#302). Also fix long long for other pointers such as in PaintBuffer. Also fix long long for uint types. Get rid of invalid/unnecessary long conversions eg. from int64 type. --- docs/Build-instructions.md | 82 +++++++++++++++++++++------ docs/Tutorial.md | 8 +++ src/browser.pyx | 31 ++++++---- src/cefpython.pyx | 15 +++-- src/client_handler/render_handler.cpp | 4 +- src/extern/cef/cef_types.pxd | 11 ++-- src/frame.pyx | 3 +- src/handlers/render_handler.pyx | 4 +- src/handlers/request_handler.pyx | 2 +- src/handlers/resource_handler.pyx | 2 +- src/paint_buffer.pyx | 4 +- src/process_message_utils.pyx | 8 +-- src/utils.pyx | 4 +- src/web_request.pyx | 8 +-- src/window_utils_linux.pyx | 13 +++-- src/window_utils_mac.pyx | 7 ++- src/window_utils_win.pyx | 17 +++--- tools/build.py | 4 +- unittests/_test_runner.py | 2 +- 19 files changed, 152 insertions(+), 77 deletions(-) diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index b0c4a049..179a970d 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -1,27 +1,33 @@ # Build instructions -__IMPORTANT NOTE__: These instructions are for the new releases of CEF Python -(Chrome v51 and later). Currently only Linux platform is being tested. -For the old v31 release that is supported on all platforms, see build -instructions on the wiki pages. +These instructions are for the new releases of CEF Python v50+. +For the old v31 release that is supported on all platforms, see +the build instructions on the wiki pages. + +If you would like to quickly build cefpython then see the +[Quick build instructions for Windows](#quick-build-instructions-for-windows) +and [Quick build instructions for Linux](#quick-build-instructions-for-linux) +sections for complete instructions. There are several types of builds you can perform: -1. You can build CEF Python using prebuilt CEF binaries that were - uploaded to GH releases (tagged eg. v51-upstream) -2. You can build both CEF Python and CEF from sources, but note +1. You can build CEF Python using prebuilt CEF binaries and libraries + that were uploaded to GH releases (tagged eg. v55-upstream) +2. You can build CEF Python using prebuilt CEF binaries from + Spotify Automated Builds. +3. You can build both CEF and CEF Python from sources, but note that Building CEF is a long process that can take hours. - In the tools/ directory there is the automate.py script that - automates building CEF. -3. You may also use prebuilt binaries from Spotify automated builds, - see the CEF automated builds section. -Before you can build CEF Python or CEF you must satisfy requirements -listed on this page. +Detailed instructions for building can be found in module doc comments +in the `automate.py` and `build.py` tools (the tools/ directory). + +Before you can build CEF Python or CEF you must satisfy some +[requirements](#requirements) listed on this page. Table of contents: -* [Build CEF Python on Linux](#build-cef-python-on-linux) +* [Quick build instructions for Windows](#quick-build-instructions-for-windows) +* [Quick build instructions for Linux](#quick-build-instructions-for-linux) * [Requirements](#requirements) * [Build CEF Python using prebuilt CEF binaries](#build-cef-python-using-prebuilt-cef-binaries) * [Build both CEF Python and CEF from sources](#build-both-cef-python-and-cef-from-sources) @@ -30,7 +36,44 @@ Table of contents: * [How to patch](#how-to-patch) -## Build CEF Python on Linux +## Quick build instructions for Windows + +Complete steps for building CEF Python v50+ using prebuilt binaries +and libraries from GitHub releases: + +1) Tested and works fine on Windows 7 64-bit + +2) Download [ninja](https://github.com/ninja-build/ninja) 1.7.2 or later + and add it to PATH. + +3) Download [cmake](https://cmake.org/download/) 3.7.2 or later and add + it to PATH. + +4) For Python 2.7 Install "Visual C++ Compiler for Python 2.7" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=44266) + +5) For Python 2.7 and when using using "Visual C++ compiler for Python 2.7" + you have to install "Visual C++ 2008 Redistributable Package (x64)" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) + +6) Install python dependencies by running: + `cd cefpython/tools/ && pip install -r requirements.txt` + (Cython version from requirements.txt must match exactly) + +7) Download 32-bit Windows binaries and libraries from + [GH releases](https://github.com/cztomczak/cefpython/tags) + tagged e.g. 'v55-upstream' when building v55. + +8) Extract the archive it in the "cefpython/build/" directory. + +9) Build cefpython and run examples (xx.x is version number eg. 55.4): +``` +cd cefpython/build/ +python ../tools/build.py xx.x +``` + + +## Quick build instructions for Linux Complete steps for building CEF Python v50+ using prebuilt binaries from GitHub releases: @@ -47,18 +90,19 @@ binaries from GitHub releases: (Cython version from requirements.txt must match exactly) 5) Download 64-bit Linux binaries and libraries from - [GH releases](https://github.com/cztomczak/cefpython/releases) - tagged e.g. 'v50-upstream' when building v50. + [GH releases](https://github.com/cztomczak/cefpython/tags) + tagged e.g. 'v55-upstream' when building v55. 6) Extract it in the cefpython/build/ directory and rename the extracted directory to "cef_linux64". -8) Build cefpython and run examples (xx.x is version e.g. 50.0): +7) Build cefpython and run examples (xx.x is version e.g. 50.0): ``` cd cefpython/src/linux/ python compile.py xx.x ``` + ## Requirements Below are platform specific requirements. Do these first before @@ -69,6 +113,8 @@ __Windows__ * Install an appropriate MS compiler for a specific Python version: https://wiki.python.org/moin/WindowsCompilers +* For Python 2.7 install "Microsoft Visual C++ Compiler for Python 2.7" + from [here](https://www.microsoft.com/en-us/download/details.aspx?id=44266) * 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) diff --git a/docs/Tutorial.md b/docs/Tutorial.md index 04ecf74e..e659b263 100644 --- a/docs/Tutorial.md +++ b/docs/Tutorial.md @@ -9,6 +9,14 @@ Table of contents: * [Hello world](#hello-world) * [CEF's multiprocess architecture](#cefs-multiprocess-architecture) * [Handling Python exceptions](#handling-python-exceptions) +* [Message loop](#message-loop) +* [Settings](#settings) +* [Handlers](#handlers) +* [Javascript integration](#javascript-integration) +* [Plugins](#plugins) +* [Helper functions](#helper-functions) +* [Build executable](#build-executable) +* [What's next?](#whats-next) ## Install and download examples diff --git a/src/browser.pyx b/src/browser.pyx index 5907dd72..4a38f0d8 100644 --- a/src/browser.pyx +++ b/src/browser.pyx @@ -107,7 +107,7 @@ cpdef PyBrowser GetBrowserByWindowHandle(WindowHandle windowHandle): for browserId in g_pyBrowsers: pyBrowser = g_pyBrowsers[browserId] if (pyBrowser.GetWindowHandle() == windowHandle or - pyBrowser.GetUserData("__outerWindowHandle") == long(windowHandle)): + pyBrowser.GetUserData("__outerWindowHandle") == windowHandle): return pyBrowser return None @@ -312,7 +312,7 @@ cdef class PyBrowser: cpdef object GetFrameByIdentifier(self, object identifier): return GetPyFrame(self.GetCefBrowser().get().GetFrame( - long(identifier))) + identifier)) cpdef list GetFrameNames(self): assert IsThread(TID_UI), ( @@ -418,7 +418,7 @@ cdef class PyBrowser: IF UNAME_SYSNAME == "Linux": x11.SetX11WindowBounds(self.GetCefBrowser(), x, y, width, height) ELSE: - raise Exception("SetBounds() not implemented on this platform") + NonCriticalError("SetBounds() not implemented on this platform") cpdef py_void SetFocus(self, enable): self.GetCefBrowserHost().get().SetFocus(bool(enable)) @@ -430,13 +430,19 @@ cdef class PyBrowser: self.GetCefBrowserHost().get().SetZoomLevel(zoomLevel) cpdef py_void ShowDevTools(self): - cdef CefWindowInfo windowInfo - cdef CefRefPtr[ClientHandler] clientHandler =\ + cdef CefWindowInfo window_info + IF UNAME_SYSNAME == "Windows": + # On Windows with empty window_info structure the devtools + # window doesn't appear. + window_info.SetAsPopup( + self.GetOpenerWindowHandle(), + PyToCefStringValue("DevTools")) + cdef CefRefPtr[ClientHandler] client_handler =\ new ClientHandler() cdef CefBrowserSettings settings cdef CefPoint inspect_element_at self.GetCefBrowserHost().get().ShowDevTools( - windowInfo, clientHandler, settings, + window_info, client_handler, settings, inspect_element_at) cpdef py_void StopLoad(self): @@ -454,7 +460,8 @@ cdef class PyBrowser: cpdef py_void ToggleFullscreen_Windows(self): cdef WindowHandle windowHandle if self.GetUserData("__outerWindowHandle"): - windowHandle = self.GetUserData("__outerWindowHandle") + windowHandle = \ + self.GetUserData("__outerWindowHandle") else: windowHandle = self.GetWindowHandle() @@ -463,7 +470,7 @@ cdef class PyBrowser: "Browser.ToggleFullscreen() failed: no window handle " "found") - cdef HWND hwnd = int(windowHandle) + cdef HWND hwnd = windowHandle cdef RECT rect cdef HMONITOR monitor cdef MONITORINFO monitorInfo @@ -533,7 +540,7 @@ cdef class PyBrowser: if "type" in pyEvent: cefEvent.type = int(pyEvent["type"]) if "modifiers" in pyEvent: - cefEvent.modifiers = long(pyEvent["modifiers"]) + cefEvent.modifiers = pyEvent["modifiers"] # Always set CefKeyEvent.windows_key_code in SendKeyEvent, even on # Linux. When sending key event for 'backspace' on Linux and setting # "native_key_code", "character", "unmodified_character" it doesn't @@ -639,7 +646,7 @@ cdef class PyBrowser: # ------------------------------------------------------------------------- cpdef py_void DragTargetDragEnter(self, DragData drag_data, int x, int y, - long long allowed_ops): + uint32 allowed_ops): cdef CefMouseEvent mouse_event mouse_event.x = x mouse_event.y = y @@ -647,7 +654,7 @@ cdef class PyBrowser: drag_data.cef_drag_data, mouse_event, allowed_ops) - cpdef py_void DragTargetDragOver(self, int x, int y, long long allowed_ops): + cpdef py_void DragTargetDragOver(self, int x, int y, uint32 allowed_ops): cdef CefMouseEvent mouse_event mouse_event.x = x mouse_event.y = y @@ -663,7 +670,7 @@ cdef class PyBrowser: mouse_event.y = y self.GetCefBrowserHost().get().DragTargetDrop(mouse_event) - cpdef py_void DragSourceEndedAt(self, int x, int y, long long operation): + cpdef py_void DragSourceEndedAt(self, int x, int y, uint32 operation): self.GetCefBrowserHost().get().DragSourceEndedAt( x, y, operation) diff --git a/src/cefpython.pyx b/src/cefpython.pyx index 634a9930..ffbcde96 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -312,11 +312,11 @@ import weakref # would be a bad idea. ctypedef object py_string -# You can't use "void" along with cpdef function returning None, it is planned to be -# added to Cython in the future, creating this virtual type temporarily. If you -# change it later to "void" then don't forget to add "except *". +# You can't use "void" along with cpdef function returning None, it is +# planned to be added to Cython in the future, creating this virtual +# type temporarily. If you change it later to "void" then don't forget +# to add "except *". ctypedef object py_void -ctypedef long long WindowHandle # noinspection PyUnresolvedReferences from cpython cimport PyLong_FromVoidPtr @@ -363,6 +363,9 @@ from libc.stdint cimport uint64_t # noinspection PyUnresolvedReferences from libc.stdint cimport uintptr_t +# noinspection PyUnresolvedReferences +ctypedef uintptr_t WindowHandle + # noinspection PyUnresolvedReferences cimport ctime @@ -388,6 +391,7 @@ from cef_types cimport ( CefSettings, CefBrowserSettings, CefRect, CefPoint, CefKeyEvent, CefMouseEvent, CefScreenInfo, PathKey, PK_DIR_EXE, PK_DIR_MODULE, + int32, uint32, int64, uint64, ) from cef_task cimport * @@ -938,7 +942,8 @@ def Shutdown(): # If the the two code blocks above, that tried to close browsers # and free CEF references, failed, then display an error about it! if len(g_pyBrowsers): - Error("Shutdown called, but there are still browser references alive!") + NonCriticalError("Shutdown called, but there are still browser" + " references alive") Debug("Shutdown()") with nogil: diff --git a/src/client_handler/render_handler.cpp b/src/client_handler/render_handler.cpp index 812f2ce9..fcbbbfeb 100644 --- a/src/client_handler/render_handler.cpp +++ b/src/client_handler/render_handler.cpp @@ -94,7 +94,7 @@ bool RenderHandler::StartDragging(CefRefPtr browser, { REQUIRE_UI_THREAD(); return RenderHandler_StartDragging(browser, drag_data, - static_cast(allowed_ops), x, y); + allowed_ops, x, y); } @@ -102,5 +102,5 @@ void RenderHandler::UpdateDragCursor(CefRefPtr browser, DragOperation operation) { REQUIRE_UI_THREAD(); - RenderHandler_UpdateDragCursor(browser, static_cast(operation)); + RenderHandler_UpdateDragCursor(browser, operation); } diff --git a/src/extern/cef/cef_types.pxd b/src/extern/cef/cef_types.pxd index 9769e0b6..1b244521 100644 --- a/src/extern/cef/cef_types.pxd +++ b/src/extern/cef/cef_types.pxd @@ -7,16 +7,19 @@ include "compile_time_constants.pxi" from libcpp cimport bool as cpp_bool # noinspection PyUnresolvedReferences from libc.stddef cimport wchar_t +# noinspection PyUnresolvedReferences +from libc.stdint cimport int32_t, uint32_t, int64_t, uint64_t from cef_string cimport cef_string_t # noinspection PyUnresolvedReferences from libc.limits cimport UINT_MAX cdef extern from "include/internal/cef_types.h": - ctypedef unsigned int uint32 - ctypedef int int32 - ctypedef long long int64 - ctypedef unsigned long long uint64 + ctypedef int32_t int32 + ctypedef uint32_t uint32 + ctypedef int64_t int64 + ctypedef uint64_t uint64 + IF UNAME_SYSNAME == "Windows": # noinspection PyUnresolvedReferences ctypedef wchar_t char16 diff --git a/src/frame.pyx b/src/frame.pyx index a8502427..ab22ba4f 100644 --- a/src/frame.pyx +++ b/src/frame.pyx @@ -21,8 +21,7 @@ cdef PyFrame GetPyFrame(CefRefPtr[CefFrame] cefFrame): Debug("GetPyFrame(): returning None") return cdef PyFrame pyFrame - # long long - cdef object frameId = cefFrame.get().GetIdentifier() + cdef object frameId = cefFrame.get().GetIdentifier() # int64 cdef int browserId = cefFrame.get().GetBrowser().get().GetIdentifier() assert (frameId and browserId), "frameId or browserId empty" cdef object uniqueFrameId = GetUniqueFrameId(browserId, frameId) diff --git a/src/handlers/render_handler.pyx b/src/handlers/render_handler.pyx index a332ff18..3204ca3b 100644 --- a/src/handlers/render_handler.pyx +++ b/src/handlers/render_handler.pyx @@ -247,7 +247,7 @@ cdef public void RenderHandler_OnScrollOffsetChanged( cdef public cpp_bool RenderHandler_StartDragging( CefRefPtr[CefBrowser] cef_browser, CefRefPtr[CefDragData] cef_drag_data, - long long allowed_ops, + uint32 allowed_ops, int x, int y ) except * with gil: cdef PyBrowser browser @@ -276,7 +276,7 @@ cdef public cpp_bool RenderHandler_StartDragging( cdef public void RenderHandler_UpdateDragCursor( CefRefPtr[CefBrowser] cef_browser, - long long operation, + uint32 operation, ) except * with gil: cdef PyBrowser browser try: diff --git a/src/handlers/request_handler.pyx b/src/handlers/request_handler.pyx index 74b3c23d..a4709d98 100644 --- a/src/handlers/request_handler.pyx +++ b/src/handlers/request_handler.pyx @@ -267,7 +267,7 @@ cdef public cpp_bool RequestHandler_OnQuotaRequest( returnValue = clientCallback( browser=pyBrowser, origin_url=pyOriginUrl, - new_size=long(newSize), + new_size=newSize, callback=CreatePyRequestCallback(cefRequestCallback)) return bool(returnValue) else: diff --git a/src/handlers/resource_handler.pyx b/src/handlers/resource_handler.pyx index c7e20929..c4f1d6cd 100644 --- a/src/handlers/resource_handler.pyx +++ b/src/handlers/resource_handler.pyx @@ -122,7 +122,7 @@ cdef public void ResourceHandler_GetResponseHeaders( if userCallback: returnValue = userCallback(pyResponse, responseLengthOut, redirectUrlOut) - (&cefResponseLength)[0] = long(responseLengthOut[0]) + (&cefResponseLength)[0] = responseLengthOut[0] if redirectUrlOut[0]: PyToCefString(redirectUrlOut[0], cefRedirectUrl) return diff --git a/src/paint_buffer.pyx b/src/paint_buffer.pyx index 3ce59d9e..0ea480d1 100644 --- a/src/paint_buffer.pyx +++ b/src/paint_buffer.pyx @@ -18,8 +18,8 @@ cdef class PaintBuffer: cdef int height cdef Py_ssize_t length - cpdef long long GetIntPointer(self) except *: - return self.buffer + cpdef uintptr_t GetIntPointer(self) except *: + return self.buffer cpdef object GetString(self, str mode="bgra", str origin="top-left"): cdef void* dest diff --git a/src/process_message_utils.pyx b/src/process_message_utils.pyx index 80e91781..314871d5 100644 --- a/src/process_message_utils.pyx +++ b/src/process_message_utils.pyx @@ -49,8 +49,8 @@ cdef list CefListValueToPyList( cdef cef_types.cef_value_type_t valueType cdef list ret = [] cdef CefRefPtr[CefBinaryValue] binaryValue - cdef cef_types.uint32 uint32_value - cdef cef_types.int64 int64_value + cdef uint32 uint32_value + cdef int64 int64_value cdef object originallyString for index in range(0, size): valueType = cefListValue.get().GetType(index) @@ -111,8 +111,8 @@ cdef dict CefDictionaryValueToPyDict( cdef CefString cefKey cdef py_string pyKey cdef CefRefPtr[CefBinaryValue] binaryValue - cdef cef_types.uint32 uint32_value - cdef cef_types.int64 int64_value + cdef uint32 uint32_value + cdef int64 int64_value cdef object originallyString while iterator != keyList.end(): cefKey = deref(iterator) diff --git a/src/utils.pyx b/src/utils.pyx index 86ced211..a3ef03aa 100644 --- a/src/utils.pyx +++ b/src/utils.pyx @@ -42,6 +42,7 @@ cpdef py_bool IsThread(int threadID): # This change is required to work with Cython 0.20. cpdef object Debug(py_string msg): + """Print debug message. Will be shown only when settings.debug=True.""" if not g_debug: return # In Python 3 str or bytes may be passed @@ -59,7 +60,8 @@ cpdef object Debug(py_string msg): print("[CEF Python] WARNING: failed writing to debug file: %s" % ( g_debugFile)) -cdef void Error(py_string msg) except *: +cdef void NonCriticalError(py_string msg) except *: + """Notify about error gently. Does not terminate application.""" # In Python 3 str or bytes may be passed if type(msg) != str and type(msg) == bytes: msg = msg.decode("utf-8", "replace") diff --git a/src/web_request.pyx b/src/web_request.pyx index d5558882..cd591ebc 100644 --- a/src/web_request.pyx +++ b/src/web_request.pyx @@ -122,8 +122,8 @@ cdef class PyWebRequest: cdef public void WebRequestClient_OnUploadProgress( int webRequestId, CefRefPtr[CefURLRequest] cefWebRequest, - cef_types.int64 current, - cef_types.int64 total + int64 current, + int64 total ) except * with gil: cdef PyWebRequest webRequest cdef object userCallback @@ -143,8 +143,8 @@ cdef public void WebRequestClient_OnUploadProgress( cdef public void WebRequestClient_OnDownloadProgress( int webRequestId, CefRefPtr[CefURLRequest] cefWebRequest, - cef_types.int64 current, - cef_types.int64 total + int64 current, + int64 total ) except * with gil: cdef PyWebRequest webRequest cdef object userCallback diff --git a/src/window_utils_linux.pyx b/src/window_utils_linux.pyx index da4030cc..9427ebb0 100644 --- a/src/window_utils_linux.pyx +++ b/src/window_utils_linux.pyx @@ -12,19 +12,20 @@ class WindowUtils: # for these methods. @staticmethod - def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(WindowHandle windowHandle, long msg, long wparam, + long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnSize(long windowHandle, long msg, long wparam, long lparam): + def OnSize(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnEraseBackground(long windowHandle, long msg, long wparam, + def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. @@ -41,11 +42,11 @@ class WindowUtils: return True @staticmethod - def gtk_plug_new(long long gdkNativeWindow): - return gtk_plug_new(gdkNativeWindow) + def gtk_plug_new(WindowHandle gdkNativeWindow): + return gtk_plug_new(gdkNativeWindow) @staticmethod - def gtk_widget_show(long long gtkWidgetPtr): + def gtk_widget_show(WindowHandle gtkWidgetPtr): with nogil: gtk_widget_show(gtkWidgetPtr) diff --git a/src/window_utils_mac.pyx b/src/window_utils_mac.pyx index 08eb7b85..7b8e8569 100644 --- a/src/window_utils_mac.pyx +++ b/src/window_utils_mac.pyx @@ -9,19 +9,20 @@ class WindowUtils: # for these methods. @staticmethod - def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(WindowHandle windowHandle, long msg, long wparam, + long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnSize(long windowHandle, long msg, long wparam, long lparam): + def OnSize(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. pass @staticmethod - def OnEraseBackground(long windowHandle, long msg, long wparam, + def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, long lparam): # Available only on Windows, but have it available on other # platforms so that PyCharm doesn't warn about unresolved reference. diff --git a/src/window_utils_win.pyx b/src/window_utils_win.pyx index c40bd383..5f8e8019 100644 --- a/src/window_utils_win.pyx +++ b/src/window_utils_win.pyx @@ -7,7 +7,7 @@ include "cefpython.pyx" class WindowUtils(object): @staticmethod - def OnSetFocus(long windowHandle, long msg, long wparam, long lparam): + def OnSetFocus(WindowHandle windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return 0 @@ -15,7 +15,7 @@ class WindowUtils(object): return 0 @staticmethod - def OnSize(long windowHandle, long msg, long wparam, long lparam): + def OnSize(WindowHandle windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: return DefWindowProc(windowHandle, msg, wparam, lparam) @@ -35,7 +35,7 @@ class WindowUtils(object): return DefWindowProc(windowHandle, msg, wparam, lparam) @staticmethod - def OnEraseBackground(long windowHandle, long msg, long wparam, + def OnEraseBackground(WindowHandle windowHandle, long msg, long wparam, long lparam): cdef PyBrowser pyBrowser = GetBrowserByWindowHandle(windowHandle) if not pyBrowser: @@ -58,7 +58,8 @@ class WindowUtils(object): cdef WindowHandle windowHandle if pyBrowser.GetUserData("__outerWindowHandle"): - windowHandle = pyBrowser.GetUserData("__outerWindowHandle") + windowHandle = \ + pyBrowser.GetUserData("__outerWindowHandle") else: windowHandle = pyBrowser.GetWindowHandle() @@ -102,7 +103,7 @@ class WindowUtils(object): iconSmall = SendMessage( windowHandle, WM_GETICON, ICON_SMALL, 0) - cdef long long parentWindowHandle + cdef WindowHandle parentWindowHandle if not iconBig and not iconSmall: parentWindowHandle = pyBrowser.GetOpenerWindowHandle() @@ -134,11 +135,11 @@ class WindowUtils(object): ICON_SMALL, parentIconSmall) @staticmethod - def GetParentHandle(long windowHandle): - return GetParent(windowHandle) + def GetParentHandle(WindowHandle windowHandle): + return GetParent(windowHandle) @staticmethod - def IsWindowHandle(long windowHandle): + def IsWindowHandle(WindowHandle windowHandle): IF UNAME_SYSNAME == "Windows": return bool(IsWindow(windowHandle)) ELSE: diff --git a/tools/build.py b/tools/build.py index 0c2a84a4..7f89faf7 100644 --- a/tools/build.py +++ b/tools/build.py @@ -504,7 +504,9 @@ def except_all_missing(content): patterns.append( r"\bcp?def\s+" "((int|short|long|double|char|unsigned|float|double|cpp_bool" - "|cpp_string|cpp_wstring|uint64_t|uintptr_t|void" + "|cpp_string|cpp_wstring|uintptr_t|void" + "|int32|uint32|int64|uint64" + "|int32_t|uint32_t|int64_t|uint64_t" "|CefString)\s+)+" "\w+\([^)]*\)\s*(with\s+(gil|nogil))?\s*:") patterns.append( diff --git a/unittests/_test_runner.py b/unittests/_test_runner.py index 8b970aca..3968a52c 100644 --- a/unittests/_test_runner.py +++ b/unittests/_test_runner.py @@ -266,7 +266,7 @@ def _print_summary(self): failed_str += ")" print(failed_str) else: - print("[_test_runner.py] OK") + print("[_test_runner.py] OK all unit tests succeeded") self._exit() def _exit(self): From 101a223464eab744dcca421d2fca188bb6a9c992 Mon Sep 17 00:00:00 2001 From: Czarek Date: Thu, 23 Feb 2017 13:41:21 +0100 Subject: [PATCH 6/8] Up-to-date complete build instructions for all three types of builds. 1. Build using prebuilt binaries and libraries from GH Releases 2. Build using binaries from Spotify Automated Builds 3. Build upstream CEF from sources --- docs/Build-instructions.md | 295 +++++++++++++++++++++++++++---------- 1 file changed, 214 insertions(+), 81 deletions(-) diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 179a970d..72ca54e8 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -7,21 +7,21 @@ the build instructions on the wiki pages. If you would like to quickly build cefpython then see the [Quick build instructions for Windows](#quick-build-instructions-for-windows) and [Quick build instructions for Linux](#quick-build-instructions-for-linux) -sections for complete instructions. +sections. These instructions are complete meaning you don't need +to read anything more from this document. Using these quick +instructions you should be able to build cefpython in less than +10 minutes. -There are several types of builds you can perform: +There are several types of builds described in this document: 1. You can build CEF Python using prebuilt CEF binaries and libraries - that were uploaded to GH releases (tagged eg. v55-upstream) + that were uploaded to GH releases 2. You can build CEF Python using prebuilt CEF binaries from Spotify Automated Builds. -3. You can build both CEF and CEF Python from sources, but note - that Building CEF is a long process that can take hours. +3. You can build upstream CEF from sources, but note that building CEF + is a long process that can take hours. -Detailed instructions for building can be found in module doc comments -in the `automate.py` and `build.py` tools (the tools/ directory). - -Before you can build CEF Python or CEF you must satisfy some +Before you can build CEF Python or CEF you must satisfy [requirements](#requirements) listed on this page. @@ -29,17 +29,23 @@ Table of contents: * [Quick build instructions for Windows](#quick-build-instructions-for-windows) * [Quick build instructions for Linux](#quick-build-instructions-for-linux) * [Requirements](#requirements) -* [Build CEF Python using prebuilt CEF binaries](#build-cef-python-using-prebuilt-cef-binaries) -* [Build both CEF Python and CEF from sources](#build-both-cef-python-and-cef-from-sources) + * [Windows](#windows) + * [Linux](#linux) + * [Mac](#mac) + * [All platforms](#all-platforms) +* [Build using prebuilt CEF binaries and libraries](#build-using-prebuilt-cef-binaries-and-libraries) +* [Build using CEF binaries from Spotify Automated Builds](#build-using-cef-binaries-from-spotify-automated-builds) +* [Build upstream CEF from sources](#build-upstream-cef-from-sources) * [Build CEF manually](#build-cef-manually) -* [CEF automated builds](#cef-automated-builds) -* [How to patch](#how-to-patch) +* [CEF Automated Builds (Spotify and Adobe)](#cef-automated-builds-spotify-and-adobe) +* [Notes](#notes) +* [How to patch mini tutorial](#how-to-patch-mini-tutorial) ## Quick build instructions for Windows Complete steps for building CEF Python v50+ using prebuilt binaries -and libraries from GitHub releases: +and libraries from GitHub Releases: 1) Tested and works fine on Windows 7 64-bit @@ -56,19 +62,30 @@ and libraries from GitHub releases: you have to install "Visual C++ 2008 Redistributable Package (x64)" from [here](https://www.microsoft.com/en-us/download/details.aspx?id=15336) -6) Install python dependencies by running: - `cd cefpython/tools/ && pip install -r requirements.txt` - (Cython version from requirements.txt must match exactly) +6) Clone cefpython and create a build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` + +7) Install python dependencies: +``` +pip install -r ../tools/requirements.txt +``` -7) Download 32-bit Windows binaries and libraries from +8) Download Windows binaries and libraries from [GH releases](https://github.com/cztomczak/cefpython/tags) - tagged e.g. 'v55-upstream' when building v55. + tagged e.g. 'v55-upstream' when building v55. The version + of the binaries must match exactly the CEF version from + the "cefpython/src/version/cef_version_win.h" file + (the CEF_VERSION constant). -8) Extract the archive it in the "cefpython/build/" directory. +8) Extract the archive in the "build/" directory. -9) Build cefpython and run examples (xx.x is version number eg. 55.4): +9) Build cefpython and run examples (xx.x is version number): ``` -cd cefpython/build/ python ../tools/build.py xx.x ``` @@ -76,29 +93,45 @@ python ../tools/build.py xx.x ## Quick build instructions for Linux Complete steps for building CEF Python v50+ using prebuilt -binaries from GitHub releases: +binaries and libraries from GitHub Releases: -1) Tested and works fine on Ubuntu 14.04 64-bit (cmake 2.8.12 and g++ 4.8.4) +1) Tested and works fine on Ubuntu 14.04 64-bit 2) Download [ninja](https://github.com/ninja-build/ninja) 1.7.1 or later and copy it to /usr/bin and chmod 755. -3) Install packages: `sudo apt-get install python-dev cmake g++ libgtk2.0-dev` +3) Install required packages (tested and works with: cmake 2.8.12 + and g++ 4.8.4): +``` +sudo apt-get install python-dev cmake g++ libgtk2.0-dev +``` -4) Install python dependencies by executing: - `cd cefpython/tools/ && sudo pip install -r requirements.txt` - (Cython version from requirements.txt must match exactly) +4) Clone cefpython and create build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` + +5) Install python dependencies: +``` +sudo pip install -r ../tools/requirements.txt +``` -5) Download 64-bit Linux binaries and libraries from +6) Download Linux binaries and libraries from [GH releases](https://github.com/cztomczak/cefpython/tags) - tagged e.g. 'v55-upstream' when building v55. + tagged e.g. 'v55-upstream' when building v55. The version + of the binaries must match exactly the CEF version from + the "cefpython/src/version/cef_version_linux.h" file + (the CEF_VERSION constant). -6) Extract it in the cefpython/build/ directory and rename the extracted - directory to "cef_linux64". +7) Extract the archive in the build/ directory and rename + the extracted directory to "cef_linux64/". -7) Build cefpython and run examples (xx.x is version e.g. 50.0): +8) Build cefpython and run examples (xx.x is version number): ``` -cd cefpython/src/linux/ +cd ../src/linux/ python compile.py xx.x ``` @@ -109,7 +142,7 @@ Below are platform specific requirements. Do these first before following instructions in the "All platforms" section that lists requirements common for all platforms. -__Windows__ +### Windows * Install an appropriate MS compiler for a specific Python version: https://wiki.python.org/moin/WindowsCompilers @@ -133,7 +166,7 @@ __Windows__ "%LocalAppData%\Programs\Common\Microsoft\Visual C++ for Python\9.0\VC\include\" -__Linux__ +### Linux * Install packages: `sudo apt-get install python-dev cmake g++ libgtk2.0-dev` * If using prebuilt binaries from Spotify automated builds and want to @@ -159,62 +192,118 @@ __Linux__ [cef/#1804](https://bitbucket.org/chromiumembedded/cef/issues/1804). -__All platforms__ +### Mac + +* MacOS 10.9+, Xcode5+ and Xcode command line tools. Only 64-bit builds + are supported. + + + +### All platforms * Install dependencies for the automate.py tool by executing: `cd tools/ && pip install -r requirements.txt` (on Linux use `sudo`). - This will install some PyPI packages including Cython. On Windows - installing Cython requires a VS compiler - see instructions above - for Windows. + This will install some PyPI packages including Cython. -## Build CEF Python using prebuilt CEF binaries +## Build using prebuilt CEF binaries and libraries -__NOT WORKING YET__ +1) Clone cefpython and create a build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` -Prebuilt binaries are available on -[GitHub Releases](https://github.com/cztomczak/cefpython/releases) -and tagged eg. 'v51-upstream'. +2) Download binaries and libraries from + [GH releases](https://github.com/cztomczak/cefpython/tags) + tagged eg. 'v55-upstream' when building v55. The version + of the binaries must match exactly the CEF version from + the "cefpython/src/version/" directory (look for CEF_VERSION + constant in .h file). -Run the automate.py tool using the --prebuilt-cef flag that will download -prebuilt binaries from GitHub Releases using version information from -the "src/version/" directory. +3) Extract the downloaded archive eg. "cef55_3.2883.1553.g80bd606_win32.zip" + in the "build/" directory (using "extract here" option) +4) Run the build.py tool (xx.x is version number): ``` -cd tools/ -python automate.py --prebuilt-cef +python ../tools/build.py xx.x ``` -You should be fine by running automate.py with the default options, but if you -need to customize the build then use the --help flag to see more. +## Build using CEF binaries from Spotify Automated Builds -## Build both CEF Python and CEF from sources +1) Clone cefpython and create a build/ directory and enter it: +``` +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ +``` + +2) Download CEF binaries from [Spotify Automated Builds](http://opensource.spotify.com/cefbuilds/index.html). + The version of the binaries must match exactly the CEF version + from the "cefpython/src/version/" directory (look for CEF_VERSION + constant in .h file). -Run the automate.py tool using the --build-cef flag. You can optionally -set how many parallel ninja jobs to run (by default cores/2) with -the --ninja-jobs flag. +3) Extract the downloaded archive eg. + "cef_binary_3.2883.1553.g80bd606_windows32.tar.bz2" + in the build/ directory (using "extract here" option) -The automate script will use version information from the "src/version/" -directory. If you would like to use a custom CEF branch then you can -use the --cef-branch flag - but note that this is only for advanced +4) Run the automate.py tool. After it completes you should see a new + directory eg. "cef55_3.2883.1553.g80bd606_win32/". +``` +python ../tools/automate.py --prebuilt-cef +``` + +5) Run the build.py tool (xx.x is version number): +``` +python ../tools/build.py xx.x +``` + + +## Build upstream CEF from sources + +Building CEF from sources is a very long process that can take several +hours depending on your CPU speed and the platform you're building on. +If you would like to speed up the process you could modify automate.py +tool and remove the `is_official_build=true` flag which slows down +builds significantly (PR to add an option for that is welcome). +You can optionally set how many parallel ninja jobs to run (by default cores/2) with the --ninja-jobs flag passed to automate.py. + +To build CEF from sources run the automate.py tool using the --build-cef +flag. The automate script will use version information from the +"cefpython/src/version/" directory. If you would like to use +a custom CEF branch +then use the --cef-branch flag, but note that this is only for advanced users as this will require updating cefpython's C++/Cython code. -If building on Linux and there are errors, see the MISSING PACKAGES -note futher down. +If building on Linux and there are errors, see the +"MISSING PACKAGES (Linux)" note futher down. -You should be fine by running automate.py with the default options, but if you -need to customize the build then use the --help flag to see more. +You should be fine by running automate.py with the default options, +but if you need to customize the build then use the --help flag to +see more options. +The commands below will build CEF from sources with custom CEF Python +patches applied and then build the CEF Python package (xx.x is version +number): ``` -cd ~/cefpython/ -mkdir build/ && cd build +git clone https://github.com/cztomczak/cefpython.git +cd cefpython/ +mkdir build/ +cd build/ python ../tools/automate.py --build-cef --ninja-jobs 6 -mv cef*_*_linux64/ cef_linux64/ -cd ../../../src/linux/ -python compile.py xx.x +python ../tools/build.py xx.x ``` +The automate.py tool should create eg. "cef55_3.2883.1553.g80bd606_win32/" +directory when it's done. Then the build.py tool will build the cefpython +module, make installer package, install the package and run unit tests +and examples. See the notes for commands for creating package installer +and/or wheel package for distribution. + __MISSING PACKAGES (Linux)__: After the chromium sources are downloaded, it will try to build cef projects and if it fails due to missing packages make sure you've installed all the required packages listed in the @@ -235,8 +324,8 @@ After dependencies are satisifed re-run automate.py. ## Build CEF manually CEF Python official binaries come with custom CEF binaries with -a few patches applied for our use case. These patches are in the -patches/ directory. +a few patches applied for our use case, see the Notes section further +down on this page. On Linux before running any of CEF tools apply the issue73 patch first. @@ -245,16 +334,16 @@ To build CEF follow the instructions on the Branches and Building CEF wiki page: https://bitbucket.org/chromiumembedded/cef/wiki/BranchesAndBuilding -After it is successfully built - apply patches, rebuild and remake +After it is successfully built, apply patches, rebuild and remake distribs. Note that CEF patches must be applied in the "download_dir/chromium/src/cef/" directory, not in the "download_dir/cef/" directory. -## CEF automated builds +## CEF Automated Builds (Spotify and Adobe) -There are two sites that provide latest builds of CEF: +There are two sites that provide automated CEF builds: * Spotify - http://opensource.spotify.com/cefbuilds/index.html * This is the new build system * Since June 2016 all builds are without tcmalloc, see @@ -264,25 +353,69 @@ There are two sites that provide latest builds of CEF: * This is the old build system. Not tested whether it builds without tcmalloc. + +## Notes + +If you would like to update CEF version in cefpython then +see complete instructions provided in +[Issue #264](https://github.com/cztomczak/cefpython/issues/264). + +When building for multiple Python versions on Linux/Mac use +pyenv to manage multiple Python installations, see +[Issue #249](https://github.com/cztomczak/cefpython/issues/249) +for details. + +Command for making installer package is (xx.x is version number): +``` +cd cefpython/build/ +python ../tools/make_installer.py xx.x +``` + +To create a wheel package from that installer package type: +``` +cd *-setup/ +python setup.py bdist_wheel +cd dist/ +ls *.whl +``` + +Optional flags for the setup.py script above: +* `--python-tag cp27` to generate Python 2.7 only package +* `--universal` to build package for multiple Python versions + (in such case you must first build multiple cefpython modules + for each Python version) + +CEF Python binaries are build using similar configuration as described +on the ["Automated Build Setup"](https://bitbucket.org/chromiumembedded/cef/wiki/AutomatedBuildSetup.md#markdown-header-platform-build-configurations) wiki page in upstream CEF. The automate.py tool incorporates most of +of the flags from these configurations. + To build the "libcef_dll_wrapper" library type these commands: ``` -cd cef_binary/ +cd cef_binary*/ mkdir build cd build/ cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release .. ninja libcef_dll_wrapper ``` -To build CEF sample applications type `ninja cefclient cefsimple`. +To build CEF sample applications type: +``` +ninja cefclient cefsimple ceftests +``` + +Official CEF Python binaries may come with additional patches applied +to CEF/Chromium depending on platform. These patches can be found +in the "cefpython/patches/" directory. Whether you need these patches +depends on your use case, they may not be required and thus you could +use the Spotify Automated Builds. Spotify builds have the issue73 patch +(no tcmalloc) applied. -Official CEF Python binaries come with additional patches to CEF/Chromium, -see the [patches/](../../../tree/master/patches) directory. Whether you -need these patches depends on your use case, they may not be required -and thus you could use the Spotify binaries. Spotify builds have the -issue73 patch (no tcmalloc) applied. +Currently (February 2017) only Linux releases have the custom +patches applied. Windows and Mac releases use CEF binaries from +Spotify Automated Builds. -## How to patch +## How to patch mini tutorial Create a patch from unstaged changes in current directory: ``` From 6b24eb198159a1e18c6902bb3f219a58276bbd21 Mon Sep 17 00:00:00 2001 From: cztomczak Date: Fri, 24 Feb 2017 22:38:55 +0100 Subject: [PATCH 7/8] Build v55 on Mac Part 1 (#295)... Successfully built the cefpython module. There are still some warnings and issues to resolve, but it looks good. Update makefiles. Update installer setup for Mac. Update Mac requirements in build instructions. Add --fast-build option to automate.py. --- docs/Build-instructions.md | 9 +-- src/client_handler/Makefile | 12 ++- src/client_handler/lifespan_handler.cpp | 2 + src/compile_time_constants.pxi | 2 +- src/extern/cef/cef_mac.pxd | 2 +- src/linux/setup/setup.py | 4 +- src/subprocess/Makefile | 26 +++--- src/subprocess/Makefile-libcefpythonapp | 2 +- src/version/cef_version_mac.h | 102 ++++++++++++++++++++++++ tools/automate.py | 12 ++- tools/build.py | 83 +++++++++---------- tools/build_module.py | 29 +++---- tools/installer/cefpython3.__init__.py | 32 +++++--- 13 files changed, 226 insertions(+), 91 deletions(-) create mode 100644 src/version/cef_version_mac.h diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index 72ca54e8..d22d0263 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -198,7 +198,6 @@ requirements common for all platforms. are supported. - ### All platforms * Install dependencies for the automate.py tool by executing: @@ -267,10 +266,10 @@ python ../tools/build.py xx.x Building CEF from sources is a very long process that can take several hours depending on your CPU speed and the platform you're building on. -If you would like to speed up the process you could modify automate.py -tool and remove the `is_official_build=true` flag which slows down -builds significantly (PR to add an option for that is welcome). -You can optionally set how many parallel ninja jobs to run (by default cores/2) with the --ninja-jobs flag passed to automate.py. +To speed up the process you can pass the --fast-build flag, however +in such case result binaries won't be optimized. +You can optionally set how many parallel ninja jobs to run (by default +cores/2) with the --ninja-jobs flag passed to automate.py. To build CEF from sources run the automate.py tool using the --build-cef flag. The automate script will use version information from the diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile index 08abf696..5340da3d 100644 --- a/src/client_handler/Makefile +++ b/src/client_handler/Makefile @@ -10,17 +10,21 @@ UNAME_S = $(shell uname -s) CC = g++ -CCFLAGS = -fPIC -std=gnu++11 -Wall -Werror $(CEF_CCFLAGS) +CCFLAGS = -fPIC -std=c++11 -Wall -Werror $(CEF_CCFLAGS) SRC = client_handler.cpp cookie_visitor.cpp resource_handler.cpp \ web_request_client.cpp string_visitor.cpp request_context_handler.cpp \ - task.cpp x11.cpp context_menu_handler.cpp display_handler.cpp \ + task.cpp context_menu_handler.cpp display_handler.cpp \ download_handler.cpp focus_handler.cpp js_dialog_handler.cpp \ keyboard_handler.cpp lifespan_handler.cpp load_handler.cpp \ render_handler.cpp request_handler.cpp OBJ = $(SRC:.cpp=.o) +ifeq ($(UNAME_S), Linux) + OBJ += x11.o +endif + ifeq ($(UNAME_S), Darwin) OBJ += util_mac.o endif @@ -56,6 +60,10 @@ $(OUT): $(OBJ) @echo [CLIENT HANDLER] Creating library $(OUT) from $(OBJ)... ar rcs $(OUT) $(OBJ) +x11.o: x11.cpp + @echo [CLIENT HANDLER] Building $@ from $<... + $(CC) $(CCFLAGS) $(INC) -c $< -o $@ + util_mac.o: util_mac.mm @echo [CLIENT HANDLER] Building $@ from $<... $(CC) $(CCFLAGS) $(INC) -c $< -o $@ diff --git a/src/client_handler/lifespan_handler.cpp b/src/client_handler/lifespan_handler.cpp index 4b9f24a8..d77a1f0e 100644 --- a/src/client_handler/lifespan_handler.cpp +++ b/src/client_handler/lifespan_handler.cpp @@ -1,7 +1,9 @@ // Copyright (c) 2016 CEF Python. See the Authors and License files. #include "lifespan_handler.h" +#if defined(OS_WIN) #include "dpi_aware.h" +#endif #include "LOG_DEBUG.h" diff --git a/src/compile_time_constants.pxi b/src/compile_time_constants.pxi index 10ec798a..05306be6 100644 --- a/src/compile_time_constants.pxi +++ b/src/compile_time_constants.pxi @@ -1,3 +1,3 @@ # This file was generated by setup.py -DEF UNAME_SYSNAME = "Windows" +DEF UNAME_SYSNAME = "Darwin" DEF PY_MAJOR_VERSION = 2 diff --git a/src/extern/cef/cef_mac.pxd b/src/extern/cef/cef_mac.pxd index 60216fcc..2639eb39 100644 --- a/src/extern/cef/cef_mac.pxd +++ b/src/extern/cef/cef_mac.pxd @@ -6,7 +6,7 @@ include "compile_time_constants.pxi" from libcpp cimport bool as cpp_bool -cdef extern from "include/internal/cef_linux.h": +cdef extern from "include/internal/cef_mac.h": ctypedef void* CefWindowHandle ctypedef void* CefCursorHandle diff --git a/src/linux/setup/setup.py b/src/linux/setup/setup.py index d06a688f..c9a4494d 100644 --- a/src/linux/setup/setup.py +++ b/src/linux/setup/setup.py @@ -16,7 +16,7 @@ # Fast mode disables optimization flags FAST = True print("FAST mode On") - COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=gnu++11'] + COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=c++11'] LINK_OPTIMIZE_FLAGS = ['-flto'] else: FAST = False @@ -25,7 +25,7 @@ # prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/6687630/ COMPILE_OPTIMIZE_FLAGS = ['-flto', '-fdata-sections', '-ffunction-sections', - '-std=gnu++11'] + '-std=c++11'] LINK_OPTIMIZE_FLAGS = ['-flto', '-Wl,--gc-sections'] diff --git a/src/subprocess/Makefile b/src/subprocess/Makefile index fcaad726..75fdeb36 100644 --- a/src/subprocess/Makefile +++ b/src/subprocess/Makefile @@ -32,27 +32,33 @@ ifeq ($(UNAME_S), Linux) -L./../../build/cef_linux64/lib \ -L./../../build/cef_linux32/lib else ifeq ($(UNAME_S), Darwin) - LIB_DIRS = -L./../../build/cef_mac64/bin \ - -L./../../build/cef_mac32/bin \ - -L./../../build/cef_mac64/lib \ - -L./../../build/cef_mac32/lib + LIB_DIRS = -F$(CEF_BIN) \ + -L$(CEF_LIB) endif ifeq ($(UNAME_S), Linux) CPP_FILES = print_handler_gtk.cpp - LIBS = -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 -else + LIBS = -lcef -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 +else ifeq ($(UNAME_S), Darwin) CPP_FILES = - LIBS = + LIBS = -framework Chromium\ Embedded\ Framework endif -CCFLAGS = -g -std=gnu++11 -Wall -Werror -DRENDERER_PROCESS $(CEF_CCFLAGS) + +CCFLAGS = -g -std=c++11 -Wall -Werror -DRENDERER_PROCESS $(CEF_CCFLAGS) + +ifeq ($(UNAME_S), Darwin) + MACFLAGS = -O3 -DNDEBUG -stdlib=libstdc++ \ + -Wl,-search_paths_first -Wl,-ObjC -Wl,-pie -Wl,-dead_strip +else + MACFLAGS = +endif subprocess: # -fPIC is required only for libraries included by Cython. @echo [SUBPROCESS] Building the 'subprocess' executable - g++ $(CCFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ + g++ $(CCFLAGS) $(MACFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ v8function_handler.cpp v8utils.cpp javascript_callback.cpp \ $(CPP_FILES) \ - $(LIBS) -lcef -lcef_dll_wrapper -o subprocess -Wl,-rpath,. + $(LIBS) -lcef_dll_wrapper -o subprocess -Wl,-rpath,. diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index b52157b7..5264a9c5 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -10,7 +10,7 @@ UNAME_S = $(shell uname -s) CC = g++ -CCFLAGS = -fPIC -std=gnu++11 -Wall -Werror -DBROWSER_PROCESS \ +CCFLAGS = -fPIC -std=c++11 -Wall -Werror -DBROWSER_PROCESS \ $(CEF_CCFLAGS) ifeq ($(UNAME_S), Linux) diff --git a/src/version/cef_version_mac.h b/src/version/cef_version_mac.h new file mode 100644 index 00000000..3a20c3f4 --- /dev/null +++ b/src/version/cef_version_mac.h @@ -0,0 +1,102 @@ +// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the name Chromium Embedded +// Framework nor the names of its contributors may be used to endorse +// or promote products derived from this software without specific prior +// written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --------------------------------------------------------------------------- +// +// This file is generated by the make_version_header.py tool. +// + +#ifndef CEF_INCLUDE_CEF_VERSION_H_ +#define CEF_INCLUDE_CEF_VERSION_H_ + +#define CEF_VERSION "3.2883.1553.g80bd606" +#define CEF_VERSION_MAJOR 3 +#define CEF_COMMIT_NUMBER 1553 +#define CEF_COMMIT_HASH "80bd6062d7ac4c5fd1d7bc7ee78e8e59d4a040aa" +#define COPYRIGHT_YEAR 2017 + +#define CHROME_VERSION_MAJOR 55 +#define CHROME_VERSION_MINOR 0 +#define CHROME_VERSION_BUILD 2883 +#define CHROME_VERSION_PATCH 87 + +#define DO_MAKE_STRING(p) #p +#define MAKE_STRING(p) DO_MAKE_STRING(p) + +#ifndef APSTUDIO_HIDDEN_SYMBOLS + +#include "include/internal/cef_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// The API hash is created by analyzing CEF header files for C API type +// definitions. The hash value will change when header files are modified +// in a way that may cause binary incompatibility with other builds. The +// universal hash value will change if any platform is affected whereas the +// platform hash values will change only if that particular platform is +// affected. +#define CEF_API_HASH_UNIVERSAL "87b7eefcb86c87b28f86bfd7919f7d7a6cffc0d8" +#if defined(OS_WIN) +#define CEF_API_HASH_PLATFORM "00823905486d7b7222da5654fe35d2d15f65543a" +#elif defined(OS_MACOSX) +#define CEF_API_HASH_PLATFORM "f0180f006643782254250f34e858b98110a40e6e" +#elif defined(OS_LINUX) +#define CEF_API_HASH_PLATFORM "14b19454a4231fa10a77b8955954dc95f073af6b" +#endif + +// Returns CEF version information for the libcef library. The |entry| +// parameter describes which version component will be returned: +// 0 - CEF_VERSION_MAJOR +// 1 - CEF_COMMIT_NUMBER +// 2 - CHROME_VERSION_MAJOR +// 3 - CHROME_VERSION_MINOR +// 4 - CHROME_VERSION_BUILD +// 5 - CHROME_VERSION_PATCH +/// +CEF_EXPORT int cef_version_info(int entry); + +/// +// Returns CEF API hashes for the libcef library. The returned string is owned +// by the library and should not be freed. The |entry| parameter describes which +// hash value will be returned: +// 0 - CEF_API_HASH_PLATFORM +// 1 - CEF_API_HASH_UNIVERSAL +// 2 - CEF_COMMIT_HASH +/// +CEF_EXPORT const char* cef_api_hash(int entry); + +#ifdef __cplusplus +} +#endif + +#endif // APSTUDIO_HIDDEN_SYMBOLS + +#endif // CEF_INCLUDE_CEF_VERSION_H_ diff --git a/tools/automate.py b/tools/automate.py index 0d966067..f6dd9fb0 100644 --- a/tools/automate.py +++ b/tools/automate.py @@ -20,6 +20,7 @@ Usage: automate.py (--prebuilt-cef | --build-cef) + [--fast-build FAST_BUILD] [--force-chromium-update FORCE_CHROMIUM_UPDATE] [--no-cef-update NO_CEF_UPDATE] [--cef-branch BRANCH] [--cef-commit COMMIT] @@ -34,6 +35,7 @@ binaries for Linux are built on Ubuntu. --build-cef Whether to build CEF from sources with the cefpython patches applied. + --fast-build Fast build with is_official_build=False --force-chromium-update Force Chromium update (gclient sync etc). --no-cef-update Do not update CEF sources (by default both cef/ directories are deleted on every run). @@ -73,6 +75,7 @@ class Options(object): # From command-line prebuilt_cef = False build_cef = False + fast_build = False force_chromium_update = False no_cef_update = False cef_branch = "" @@ -354,8 +357,11 @@ def build_cef_projects(): print("[automate.py] Build cefclient, cefsimple, ceftests") # Cmake command = prepare_build_command() - command.extend(["cmake", "-G", "Ninja", - "-DCMAKE_BUILD_TYPE="+Options.build_type, ".."]) + command.extend(["cmake", "-G", "Ninja"]) + command.append("-DCMAKE_BUILD_TYPE="+Options.build_type) + if MAC: + command.append("-DPROJECT_ARCH=x86_64") + command.append("..") run_command(command, Options.build_cefclient_dir) print("[automate.py] OK") # Ninja @@ -695,7 +701,7 @@ def getenv(): # To perform an official build set GYP_DEFINES=buildtype=Official. # This will disable debugging code and enable additional link-time # optimizations in Release builds. - if Options.release_build: + if Options.release_build and not Options.fast_build: env["GN_DEFINES"] += " is_official_build=true" # Modifications to automate-git.py env["CEFPYTHON_NINJA_JOBS"] = str(Options.ninja_jobs) diff --git a/tools/build.py b/tools/build.py index 7f89faf7..6fcbb5f4 100644 --- a/tools/build.py +++ b/tools/build.py @@ -35,10 +35,10 @@ # 6. More commands: http://docs.cython.org/src/userguide/debugging.html # This will not show "Segmentation fault" error message: -# | subprocess.call(["python", "./wxpython.py"]) -# You need to call it with shell=True for this kind of -# error message to be shown: -# | subprocess.call("python wxpython.py", shell=True) +# > subprocess.call(["python", "./wxpython.py"]) +# You need to call it with command as string and shell=True +# for this kind of error message to be shown: +# > subprocess.call("python wxpython.py", shell=True) from common import * import sys @@ -84,11 +84,17 @@ def main(): check_cython_version() command_line_args() check_directories() - fix_cefpython_h() - if WINDOWS: - compile_cpp_projects_windows() - elif MAC or LINUX: - compile_cpp_projects_unix() + if os.path.exists(CEFPYTHON_H): + fix_cefpython_h() + if WINDOWS: + compile_cpp_projects_windows() + elif MAC or LINUX: + compile_cpp_projects_unix() + else: + print("[build.py] INFO: Looks like first run, as cefpython.h" + " is missing. Skip building C++ projects.") + global FIRST_RUN + FIRST_RUN = True clear_cache() copy_and_fix_pyx_files() create_version_pyx_file() @@ -103,9 +109,6 @@ def setup_environ(): include path. Set Mac compiler options. Etc.""" print("[build.py] Setup environment variables") - if not WINDOWS: - return - # PATH if WINDOWS: path = [ @@ -134,6 +137,11 @@ def setup_environ(): print("[build.py] environ AdditionalLibraryDirectories: {lib}" .format(lib=os.environ["AdditionalLibraryDirectories"])) + # Mac env variables for makefiles + if MAC: + os.environ["CEF_BIN"] = os.path.join(CEF_BINARIES_LIBRARIES, "bin") + os.environ["CEF_LIB"] = os.path.join(CEF_BINARIES_LIBRARIES, "lib") + # Mac compiler options if MAC: os.environ["PATH"] = "/usr/local/bin:"+os.environ["PATH"] @@ -235,14 +243,13 @@ def fix_cefpython_h(): print("[build.py] cefpython.h was not yet generated") return with open("cefpython.h", "r") as fo: - content = fo.read() - pragma = "#pragma warning(disable:4190)" - if pragma in content: - print("[build.py] cefpython.h is already fixed") - return - content = ("%s\n\n" % pragma) + content + contents = fo.read() + # Error/warning depending on compiler: + # > has C-linkage specified, but returns user-defined type + contents = contents.replace("#define __PYX_EXTERN_C extern \"C\"", + "#define __PYX_EXTERN_C extern") with open("cefpython.h", "w") as fo: - fo.write(content) + fo.write(contents) print("[build.py] Save build_cefpython/cefpython.h") @@ -288,13 +295,6 @@ 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(VS2008_VCVARS) @@ -451,7 +451,7 @@ def copy_and_fix_pyx_files(): os.remove(pyxfile) # Copying pyxfiles and reading its contents. - print("[build.py] Copying pyx files to build_cefpython/: %s" % pyxfiles) + print("[build.py] Copying pyx files to build_cefpython/") # Copy cefpython.pyx and fix includes in cefpython.pyx, eg.: # include "handlers/focus_handler.pyx" becomes include "focus_handler.pyx" @@ -467,7 +467,7 @@ def copy_and_fix_pyx_files(): print("[build.py] %s includes fixed in %s" % (subs, mainfile)) # Copy the rest of the files - print("[build.py] Fixing includes in .pyx files:") + print("[build.py] Fix includes in other .pyx files") for pyxfile in pyxfiles: newfile = "./%s" % os.path.basename(pyxfile) shutil.copy(pyxfile, newfile) @@ -489,8 +489,9 @@ def copy_and_fix_pyx_files(): content, flags=re.MULTILINE) if subs: - print("[build.py] %s includes removed in: %s" - % (subs, os.path.basename(pyxfile))) + # print("[build.py] %s includes removed in: %s" + # % (subs, os.path.basename(pyxfile))) + pass with open(pyxfile, "w") as pyxfileopened: pyxfileopened.write(content) @@ -540,24 +541,26 @@ def create_version_pyx_file(): def build_cefpython_module(): - os.chdir(BUILD_CEFPYTHON) # if DEBUG_FLAG: # ret = subprocess.call("python-dbg setup.py build_ext --inplace" # " --cython-gdb", shell=True) print("[build.py] Execute build_module.py script") print("") + + os.chdir(BUILD_CEFPYTHON) + if FAST_FLAG: - ret = subprocess.call([sys.executable, - "{tools_dir}/build_module.py" - .format(tools_dir=TOOLS_DIR), - "build_ext", "--inplace", "--fast"], + ret = subprocess.call("{python} {tools_dir}/build_module.py" + " build_ext --fast" + .format(python=sys.executable, + tools_dir=TOOLS_DIR), shell=True) else: - ret = subprocess.call([sys.executable, - "{tools_dir}/build_module.py" - .format(tools_dir=TOOLS_DIR), - "build_ext", "--inplace"], + ret = subprocess.call("{python} {tools_dir}/build_module.py" + " build_ext" + .format(python=sys.executable, + tools_dir=TOOLS_DIR), shell=True) # if DEBUG_FLAG: @@ -585,7 +588,7 @@ def build_cefpython_module(): 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) + ret = subprocess.call(" ".join(args), shell=True) sys.exit(ret) else: print("[build.py] ERROR: failed to build the cefpython module") diff --git a/tools/build_module.py b/tools/build_module.py index 0d1524df..e5c72668 100644 --- a/tools/build_module.py +++ b/tools/build_module.py @@ -77,36 +77,31 @@ def set_compiler_options(options): # # The above warning LNK4217 is caused by the warning below which occurs # when building the client_handler.lib static library: - # - # cefpython.h(36): warning C4190: 'RequestHandler_GetResourceHandler' - # has C-linkage specified, but returns UDT 'CefRefPtr' which is - # incompatible with C - # - # The C4190 warning is disabled with pragma in cefpython.h, see the - # fix_cefpython_h() in the build.py script. - extra_compile_args.extend(['/EHsc']) - extra_link_args.extend(['/ignore:4217']) + extra_compile_args.extend(["/EHsc"]) + extra_link_args.extend(["/ignore:4217"]) if LINUX: if len(sys.argv) > 1 and "--fast" in sys.argv: sys.argv.remove("--fast") # Fast mode disables optimization flags print("[build_module.py] FAST mode On") - extra_compile_args.extend(['-flto', '-std=gnu++11']) - extra_link_args.extend(['-flto']) + extra_compile_args.extend(["-flto", "-std=c++11"]) + extra_link_args.extend(["-flto"]) else: # Fix "ImportError ... undefined symbol ..." caused by CEF's # include/base/ headers by adding the -flto flag (Issue #230). # Unfortunately -flto prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/ # 6687630/ . - extra_compile_args.extend(['-flto', '-fdata-sections', - '-ffunction-sections', '-std=gnu++11']) - extra_link_args.extend(['-flto', '-Wl,--gc-sections']) + extra_compile_args.extend(["-flto", "-fdata-sections", + "-ffunction-sections", "-std=c++11"]) + extra_link_args.extend(["-flto", "-Wl,--gc-sections"]) if MAC: - os.environ["CC"] = "gcc" - os.environ["CXX"] = "g++" + extra_compile_args.extend(["-std=c++11"]) + # extra_link_args.extend([]) + # os.environ["CC"] = "gcc" + # os.environ["CXX"] = "g++" options["extra_compile_args"] = extra_compile_args options["extra_link_args"] = extra_link_args @@ -254,7 +249,7 @@ def get_ext_modules(options): "c_string_encoding": "utf-8", }, - language='c++', + language="c++", include_dirs=options["include_dirs"], library_dirs=options["library_dirs"], diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index 5e794a0a..67ce48ba 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -10,7 +10,7 @@ import ctypes import platform -__all__ = ["cefpython", "wx"] +__all__ = ["cefpython"] # Disabled: "wx" __version__ = "{{VERSION}}" __author__ = "The CEF Python authors" @@ -26,10 +26,12 @@ 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 + +# On Mac it works without setting library paths. Better not set it, +# as maybe user's app will set it itself. +# > os.environ["DYLD_LIBRARY_PATH"] = package_dir +# > os.environ["DYLD_FRAMEWORK_PATH"] = package_dir # This env variable will be returned by cefpython.GetModuleDirectory(). os.environ["CEFPYTHON3_PATH"] = package_dir @@ -40,23 +42,35 @@ # 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") + cef_framework = "Chromium Embedded Framework.framework" + libcef_name = "Chromium Embedded Framework" + # Search for it in current directory or in ../Frameworks/ dir + # in case this is user's app packaged for distribution. + libcef1 = os.path.join(package_dir, cef_framework, libcef_name) + libcef2 = os.path.join(package_dir, "..", "Frameworks", cef_framework, + libcef_name) + if os.path.exists(libcef1): + libcef = libcef1 + elif os.path.exists(libcef2): + libcef = libcef2 + else: + raise Exception("Can't find: " + cef_framework) 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): +if sys.version_info[:2] == (2, 8): # noinspection PyUnresolvedReferences from . import cefpython_py27 as cefpython -elif (3, 4) <= sys.version_info < (3, 5): +elif sys.version_info[:2] == (3, 4): # noinspection PyUnresolvedReferences from . import cefpython_py34 as cefpython -elif (3, 5) <= sys.version_info < (3, 6): +elif sys.version_info[:2] == (3, 5): # noinspection PyUnresolvedReferences from . import cefpython_py35 as cefpython -elif (3, 6) <= sys.version_info < (3, 7): +elif sys.version_info[:2] == (3, 6): # noinspection PyUnresolvedReferences from . import cefpython_py36 as cefpython else: From eeef09ab8e3d398e5dc6714efdbcde921d33704d Mon Sep 17 00:00:00 2001 From: cztomczak Date: Mon, 27 Feb 2017 16:00:14 +0100 Subject: [PATCH 8/8] Build v55 on Mac Part 2 (#295)... Update build tools and makefiles. Link to libc++ and libc++abi to avoid undefined symbol error. Fix visibility: PyMODINIT_FUNC. Minimum Mac version: 10.7. Do not use ctypes.CDLL on Mac, load CEF framework statically. Both Mac and Linux: Add -DNDEBUG and -O3 optimization flags. libcef_dll_wrapper needs to be built using this command: cmake -G "Ninja" -DPROJECT_ARCH="x86_64" \ -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DCMAKE_BUILD_TYPE=Release .. ninja libcef_dll_wrapper --- docs/Build-instructions.md | 2 + src/__version__.pyx | 2 - src/cefpython.pyx | 12 +- src/client_handler/Makefile | 37 ++--- src/cpp_utils/Makefile | 5 +- src/linux/setup/setup.py | 4 +- src/subprocess/Makefile | 20 +-- src/subprocess/Makefile-libcefpythonapp | 33 ++-- tools/build.py | 109 +++++++++---- tools/build_module.py | 200 +++++++++++++++++++----- tools/common.py | 12 ++ tools/installer/cefpython3.__init__.py | 31 +--- 12 files changed, 312 insertions(+), 155 deletions(-) delete mode 100644 src/__version__.pyx diff --git a/docs/Build-instructions.md b/docs/Build-instructions.md index d22d0263..2ee531ae 100644 --- a/docs/Build-instructions.md +++ b/docs/Build-instructions.md @@ -196,6 +196,8 @@ requirements common for all platforms. * MacOS 10.9+, Xcode5+ and Xcode command line tools. Only 64-bit builds are supported. +* Upgrade setuptools package to latest version otherwise there will be + problems with Cython: `sudo pip install --upgrade setuptools` ### All platforms diff --git a/src/__version__.pyx b/src/__version__.pyx deleted file mode 100644 index 52d26ce1..00000000 --- a/src/__version__.pyx +++ /dev/null @@ -1,2 +0,0 @@ -# A dummy file. The compile script generates a real __version__.pyx -# in the setup/ directory. diff --git a/src/cefpython.pyx b/src/cefpython.pyx index ffbcde96..9d641ebe 100644 --- a/src/cefpython.pyx +++ b/src/cefpython.pyx @@ -246,12 +246,8 @@ END OF: CHANGES in CEF since v31..v47. # Includes being made in other .pyx files are allowed to help # IDE completion, but will be removed during cython compilation. -# Version file is generated by the compile.bat/compile.py script. -include "__version__.pyx" - include "compile_time_constants.pxi" - # ----------------------------------------------------------------------------- # IMPORTS @@ -503,7 +499,11 @@ include "command_line.pyx" include "app.pyx" include "drag_data.pyx" include "helpers.pyx" -include "image.pyx" + +# Currently used only on Linux via DragData. Do not include on other +# platforms otherwise warning about unused function appears. +IF UNAME_SYSNAME == "Linux": + include "image.pyx" # Handlers include "handlers/browser_process_handler.pyx" @@ -645,7 +645,7 @@ def Initialize(applicationSettings=None, commandLineSwitches=None, **kwargs): if "resources_dir_path" not in applicationSettings: applicationSettings["resources_dir_path"] = module_dir if platform.system() == "Darwin": - applicationSettings["resources_dir_path"] = module_dir+"/Resources" + pass # TODO: Check if this needs to be set in v56+ if "browser_subprocess_path" not in applicationSettings: applicationSettings["browser_subprocess_path"] = os.path.join( module_dir, "subprocess") diff --git a/src/client_handler/Makefile b/src/client_handler/Makefile index 5340da3d..f0b9a5a4 100644 --- a/src/client_handler/Makefile +++ b/src/client_handler/Makefile @@ -4,30 +4,29 @@ # -Wall - show important warnings # -Werror - treat warnings as errors -# Cython compiler options: +# Cython compiler options on Linux: # -fPIC -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions \ # -Wl,-z,relro UNAME_S = $(shell uname -s) -CC = g++ -CCFLAGS = -fPIC -std=c++11 -Wall -Werror $(CEF_CCFLAGS) +CCFLAGS = -fPIC $(CEF_CCFLAGS) + +ifeq ($(UNAME_S), Linux) + SRC_MORE = x11.cpp +else ifeq ($(UNAME_S), Darwin) + SRC_MORE = util_mac.mm +endif SRC = client_handler.cpp cookie_visitor.cpp resource_handler.cpp \ web_request_client.cpp string_visitor.cpp request_context_handler.cpp \ task.cpp context_menu_handler.cpp display_handler.cpp \ download_handler.cpp focus_handler.cpp js_dialog_handler.cpp \ keyboard_handler.cpp lifespan_handler.cpp load_handler.cpp \ - render_handler.cpp request_handler.cpp - -OBJ = $(SRC:.cpp=.o) - -ifeq ($(UNAME_S), Linux) - OBJ += x11.o -endif + render_handler.cpp request_handler.cpp \ + $(SRC_MORE) -ifeq ($(UNAME_S), Darwin) - OBJ += util_mac.o -endif +OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o)) +.SUFFIXES: .cpp .mm .o OUT = libclient_handler.a @@ -52,21 +51,19 @@ INC = -I./../ -I./../common/ -I/usr/include/python2.7 \ -I/usr/lib/gtk-2.0/gtk-unix-print-2.0 \ -I/usr/lib/glib-2.0/include -.cpp.o: - @echo [CLIENT HANDLER] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ $(OUT): $(OBJ) @echo [CLIENT HANDLER] Creating library $(OUT) from $(OBJ)... ar rcs $(OUT) $(OBJ) -x11.o: x11.cpp +.cpp.o: @echo [CLIENT HANDLER] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ -util_mac.o: util_mac.mm +.mm.o: @echo [CLIENT HANDLER] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ + clean: @echo [CLIENT HANDLER] Cleaning... diff --git a/src/cpp_utils/Makefile b/src/cpp_utils/Makefile index 9e589e80..338fd960 100644 --- a/src/cpp_utils/Makefile +++ b/src/cpp_utils/Makefile @@ -1,5 +1,4 @@ -CC = g++ -CCFLAGS = -g $(CEF_CCFLAGS) +CCFLAGS = -fPIC $(CEF_CCFLAGS) SRC = PaintBuffer.cpp OBJ = $(SRC:.cpp=.o) @@ -15,7 +14,7 @@ INC = -I./../ -I/usr/include/gtk-2.0 \ -I/usr/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include .cpp.o: - $(CC) -fPIC $(INC) $(CCFLAGS) -c $< -o $@ + $(CXX) $(INC) $(CCFLAGS) -c $< -o $@ $(OUT): $(OBJ) ar rcs $(OUT) $(OBJ) diff --git a/src/linux/setup/setup.py b/src/linux/setup/setup.py index c9a4494d..d06a688f 100644 --- a/src/linux/setup/setup.py +++ b/src/linux/setup/setup.py @@ -16,7 +16,7 @@ # Fast mode disables optimization flags FAST = True print("FAST mode On") - COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=c++11'] + COMPILE_OPTIMIZE_FLAGS = ['-flto', '-std=gnu++11'] LINK_OPTIMIZE_FLAGS = ['-flto'] else: FAST = False @@ -25,7 +25,7 @@ # prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/6687630/ COMPILE_OPTIMIZE_FLAGS = ['-flto', '-fdata-sections', '-ffunction-sections', - '-std=c++11'] + '-std=gnu++11'] LINK_OPTIMIZE_FLAGS = ['-flto', '-Wl,--gc-sections'] diff --git a/src/subprocess/Makefile b/src/subprocess/Makefile index 75fdeb36..c4d73a5b 100644 --- a/src/subprocess/Makefile +++ b/src/subprocess/Makefile @@ -41,24 +41,18 @@ ifeq ($(UNAME_S), Linux) LIBS = -lcef -lgobject-2.0 -lglib-2.0 -lgtk-x11-2.0 else ifeq ($(UNAME_S), Darwin) CPP_FILES = - LIBS = -framework Chromium\ Embedded\ Framework -endif - - -CCFLAGS = -g -std=c++11 -Wall -Werror -DRENDERER_PROCESS $(CEF_CCFLAGS) - -ifeq ($(UNAME_S), Darwin) - MACFLAGS = -O3 -DNDEBUG -stdlib=libstdc++ \ - -Wl,-search_paths_first -Wl,-ObjC -Wl,-pie -Wl,-dead_strip -else - MACFLAGS = + # Include framework before libcef_dll_wrapper + LIBS = -framework "Chromium Embedded Framework" endif +CCFLAGS = -DRENDERER_PROCESS $(CEF_CCFLAGS) subprocess: # -fPIC is required only for libraries included by Cython. @echo [SUBPROCESS] Building the 'subprocess' executable - g++ $(CCFLAGS) $(MACFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ + $(CXX) $(CCFLAGS) $(INC) $(LIB_DIRS) main.cpp cefpython_app.cpp \ v8function_handler.cpp v8utils.cpp javascript_callback.cpp \ $(CPP_FILES) \ - $(LIBS) -lcef_dll_wrapper -o subprocess -Wl,-rpath,. + $(CEF_LINK_FLAGS) \ + $(LIBS) -lcef_dll_wrapper \ + -o subprocess -Wl,-rpath,. diff --git a/src/subprocess/Makefile-libcefpythonapp b/src/subprocess/Makefile-libcefpythonapp index 5264a9c5..bd8186da 100644 --- a/src/subprocess/Makefile-libcefpythonapp +++ b/src/subprocess/Makefile-libcefpythonapp @@ -9,28 +9,25 @@ # -Wl,-z,relro UNAME_S = $(shell uname -s) -CC = g++ -CCFLAGS = -fPIC -std=c++11 -Wall -Werror -DBROWSER_PROCESS \ - $(CEF_CCFLAGS) +CCFLAGS = -fPIC -DBROWSER_PROCESS $(CEF_CCFLAGS) ifeq ($(UNAME_S), Linux) - CPP_FILES = print_handler_gtk.cpp \ - main_message_loop/main_message_loop_external_pump_linux.cpp + SRC_MORE = print_handler_gtk.cpp \ + main_message_loop/main_message_loop_external_pump_linux.cpp else ifeq ($(UNAME_S), Darwin) - CPP_FILES = \ - main_message_loop/main_message_loop_external_pump_mac.mm -else - CPP_FILES = + SRC_MORE = main_message_loop/main_message_loop_external_pump_mac.mm endif - SRC = cefpython_app.cpp v8function_handler.cpp v8utils.cpp \ javascript_callback.cpp \ main_message_loop/main_message_loop.cpp \ main_message_loop/main_message_loop_std.cpp \ main_message_loop/main_message_loop_external_pump.cpp \ - $(CPP_FILES) -OBJ = $(SRC:.cpp=.o) + $(SRC_MORE) + +OBJ = $(filter %.o, $(SRC:.cpp=.o) $(SRC:.mm=.o)) +.SUFFIXES: .cpp .mm .o + OUT = libcefpythonapp.a INC = -I./../ -I./../common/ -I/usr/include/python2.7 \ @@ -54,10 +51,14 @@ INC = -I./../ -I./../common/ -I/usr/include/python2.7 \ -I/usr/lib/gtk-2.0/gtk-unix-print-2.0 \ -I/usr/lib/glib-2.0/include -.cpp.o: - @echo [CEFPYTHONAPP] Building $@ from $<... - $(CC) $(CCFLAGS) $(INC) -c $< -o $@ - $(OUT): $(OBJ) @echo [CEFPYTHONAPP] Creating library $(OUT) from $(OBJ)... ar rcs $(OUT) $(OBJ) + +.cpp.o: + @echo [CEFPYTHONAPP] Building $@ from $<... + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ + +.mm.o: + @echo [CEFPYTHONAPP] Building $@ from $<... + $(CXX) $(CCFLAGS) $(INC) -c $< -o $@ diff --git a/tools/build.py b/tools/build.py index 6fcbb5f4..3d070d25 100644 --- a/tools/build.py +++ b/tools/build.py @@ -63,12 +63,6 @@ REBUILD_CPP = False VERSION = "" -# Module extension -if WINDOWS: - MODULE_EXT = "pyd" -else: - MODULE_EXT = "so" - # First run FIRST_RUN = False CEFPYTHON_H = os.path.join(BUILD_CEFPYTHON, "cefpython.h") @@ -97,7 +91,6 @@ def main(): FIRST_RUN = True clear_cache() copy_and_fix_pyx_files() - create_version_pyx_file() build_cefpython_module() fix_cefpython_h() install_and_run() @@ -137,6 +130,20 @@ def setup_environ(): print("[build.py] environ AdditionalLibraryDirectories: {lib}" .format(lib=os.environ["AdditionalLibraryDirectories"])) + if LINUX or MAC: + # Used in makefiles + os.environ["CEF_CCFLAGS"] = "-std=gnu++11 -DNDEBUG -Wall -Werror" + if FAST_FLAG: + os.environ["CEF_CCFLAGS"] += " -O0" + else: + os.environ["CEF_CCFLAGS"] += " -O3" + os.environ["CEF_LINK_FLAGS"] = "" + + if LINUX: + # TODO: Set CEF_CCFLAGS and CEF_LINK_FLAGS according to what is + # in upstream cefclient, see cef/cmake/cef_variables.cmake. + pass + # Mac env variables for makefiles if MAC: os.environ["CEF_BIN"] = os.path.join(CEF_BINARIES_LIBRARIES, "bin") @@ -145,12 +152,42 @@ def setup_environ(): # 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" + os.environ["CC"] = "c++" + os.environ["CXX"] = "c++" + if ARCH32: raise Exception("Python 32-bit is not supported on Mac") + os.environ["ARCHFLAGS"] = "-arch x86_64" + os.environ["CEF_CCFLAGS"] += " -arch x86_64" + os.environ["CEF_LINK_FLAGS"] += " -mmacosx-version-min=10.7" + + # -Wno-return-type-c-linkage to ignore: + # > warning: 'somefunc' has C-linkage specified, but returns + # > user-defined type 'sometype' which is incompatible with C + os.environ["CEF_CCFLAGS"] += " -Wno-return-type-c-linkage" + + # Compile against libc++ otherwise error "symbol not found" + # with cef::logging::LogMessage symbol. Also include -lc++ + # and -lc++abi libraries. + os.environ["CEF_CCFLAGS"] += " -stdlib=libc++" + + # See compile/link flags in upstream cefclient + os.environ["CEF_CCFLAGS"] += ( + " -fno-strict-aliasing" + " -fno-rtti" + " -fno-threadsafe-statics" + " -fobjc-call-cxx-cdtors" + " -fvisibility=hidden" + " -fvisibility-inlines-hidden" + ) + os.environ["CEF_LINK_FLAGS"] += ( + " -lc++" + " -lc++abi" + " -Wl,-search_paths_first" + " -Wl,-ObjC" + " -Wl,-pie" + " -Wl,-dead_strip" + ) def get_python_path(): @@ -237,6 +274,13 @@ def check_directories(): def fix_cefpython_h(): + # Fix cefpython.h to disable this warning: + # > warning: 'somefunc' has C-linkage specified, but returns + # > user-defined type 'sometype' which is incompatible with C + # On Mac this warning must be disabled using -Wno-return-type-c-linkage + # flag in makefiles. + if MAC: + return os.chdir(BUILD_CEFPYTHON) print("[build.py] Fix cefpython.h to disable warnings") if not os.path.exists("cefpython.h"): @@ -244,10 +288,11 @@ def fix_cefpython_h(): return with open("cefpython.h", "r") as fo: contents = fo.read() - # Error/warning depending on compiler: - # > has C-linkage specified, but returns user-defined type - contents = contents.replace("#define __PYX_EXTERN_C extern \"C\"", - "#define __PYX_EXTERN_C extern") + pragma = "#pragma warning(disable:4190)" + if pragma in contents: + print("[build.py] cefpython.h is already fixed") + return + contents = ("%s\n\n" % pragma) + contents with open("cefpython.h", "w") as fo: fo.write(contents) print("[build.py] Save build_cefpython/cefpython.h") @@ -389,12 +434,14 @@ def clear_cache(): print("[build.py] Clean build cache") # Cache in CEFPYTHON_BINARY directory (eg. cefpython_linux64/) os.chdir(CEFPYTHON_BINARY) - delete_files_by_pattern("./cefpython_py*.{ext}".format(ext=MODULE_EXT)) + delete_files_by_pattern("./"+MODULE_NAME_TEMPLATE + .format(pyversion="*", ext=MODULE_EXT)) # Cache in build_cefpython/ directory os.chdir(BUILD_CEFPYTHON) - delete_files_by_pattern("./cefpython_py*.{ext}".format(ext=MODULE_EXT)) + delete_files_by_pattern("./"+MODULE_NAME_TEMPLATE + .format(pyversion="*", ext=MODULE_EXT)) delete_files_by_pattern("./*.pyx") try: @@ -451,7 +498,7 @@ def copy_and_fix_pyx_files(): os.remove(pyxfile) # Copying pyxfiles and reading its contents. - print("[build.py] Copying pyx files to build_cefpython/") + print("[build.py] Copy pyx files to build_cefpython/") # Copy cefpython.pyx and fix includes in cefpython.pyx, eg.: # include "handlers/focus_handler.pyx" becomes include "focus_handler.pyx" @@ -462,9 +509,12 @@ def copy_and_fix_pyx_files(): "include \"", content, flags=re.MULTILINE) + # Add __version__ variable in cefpython.pyx + print("[build.py] Add __version__ variable to %s" % mainfile) + content = ('__version__ = "{}"\n'.format(VERSION)) + content with open("./%s" % mainfile, "w") as fo: fo.write(content) - print("[build.py] %s includes fixed in %s" % (subs, mainfile)) + print("[build.py] Fix %s includes in %s" % (subs, mainfile)) # Copy the rest of the files print("[build.py] Fix includes in other .pyx files") @@ -533,13 +583,6 @@ def except_all_missing(content): return lineNumber -def create_version_pyx_file(): - os.chdir(BUILD_CEFPYTHON) - print("[build.py] Create __version__.pyx file") - with open("__version__.pyx", "w") as fo: - fo.write('__version__ = "{}"\n'.format(VERSION)) - - def build_cefpython_module(): # if DEBUG_FLAG: # ret = subprocess.call("python-dbg setup.py build_ext --inplace" @@ -595,12 +638,14 @@ def build_cefpython_module(): sys.exit(1) # Move the cefpython module - move_file_by_pattern("./cefpython_py{pyver}*.{ext}" - .format(pyver=PYVERSION, ext=MODULE_EXT), - os.path.join(CEFPYTHON_BINARY, - "cefpython_py{pyver}.{ext}" - .format(pyver=PYVERSION, - ext=MODULE_EXT))) + module_pattern = MODULE_NAME_TEMPLATE.format(pyversion=PYVERSION+"*", + ext=MODULE_EXT) + if MAC: + module_pattern = "./build/lib*/"+module_pattern + else: + module_pattern = "./"+module_pattern + move_file_by_pattern(module_pattern, os.path.join(CEFPYTHON_BINARY, + MODULE_NAME)) print("[build.py] DONE building the cefpython module") diff --git a/tools/build_module.py b/tools/build_module.py index e5c72668..f347bc87 100644 --- a/tools/build_module.py +++ b/tools/build_module.py @@ -18,17 +18,88 @@ # Use "Extension" from Cython.Distutils so that "cython_directives" works from Cython.Distutils import build_ext, Extension from Cython.Compiler import Options +# noinspection PyUnresolvedReferences +from Cython.Compiler.ModuleNode import ModuleNode from common import * import sys import platform import Cython import os +# Must monkey patch Cython's ModuleNode to inject custom C++ code +# in the generated cefpython.cpp. This is a fix for an error on Mac: +# > ImportError: dynamic module does not define init function +# To get rid of CEF's undefined symbol error when importing module +# it was required to pass "-fvisibility=hidden" and "-Wl,-dead_strip" +# flags. However these flags cause the "initcefpython_py27" Python +# Module Initialization function to be hidden as well. To workaround +# this it is required to add default visibility attribute to the +# signature of that init function. +# +# Original definition in Python 2.7: +# | https://github.com/python/cpython/blob/2.7/Include/pyport.h +# > define PyMODINIT_FUNC extern "C" __declspec(dllexport) void +# +# Original definition in Python 3.4 / 3.5 / 3.6: +# > define PyMODINIT_FUNC extern "C" __declspec(dllexport) PyObject* + +if MAC: + g_generate_extern_c_macro_definition_old = ( + ModuleNode.generate_extern_c_macro_definition) + + def generate_extern_c_macro_definition(self, code): + # This code is written to both cefpython.h and cefpython.cpp + g_generate_extern_c_macro_definition_old(self, code) + code.putln("// Added by: cefpython/tools/build_module.py") + code.putln("#undef PyMODINIT_FUNC") + if sys.version_info[:2] == (2, 7): + code.putln("#define PyMODINIT_FUNC extern \"C\"" + " __attribute__((visibility(\"default\"))) void") + else: + code.putln("#define PyMODINIT_FUNC extern \"C\"" + " __attribute__((visibility(\"default\"))) PyObject*") + # Overwrite Cython function + ModuleNode.generate_extern_c_macro_definition = ( + generate_extern_c_macro_definition) + + +# Constants +FAST_FLAG = False + # Cython options. Stop on first error, otherwise hundreds # of errors appear in the console. Options.fast_fail = True +def main(): + global FAST_FLAG + if len(sys.argv) > 1 and "--fast" in sys.argv: + # Fast mode disables optimization flags + print("[build_module.py] FAST mode On") + FAST_FLAG = True + sys.argv.remove("--fast") + + if len(sys.argv) <= 1: + print(__doc__) + sys.exit(1) + + print("[build_module.py] Cython version: %s" % Cython.__version__) + + compile_time_constants() + options = dict() + set_compiler_options(options) + options["include_dirs"] = get_include_dirs() + options["library_dirs"] = get_library_dirs() + options["libraries"] = get_libraries() + + print("[build_module.py] Execute setup()") + setup( + name='cefpython_py%s' % PYVERSION, + cmdclass={'build_ext': build_ext}, + ext_modules=get_ext_modules(options) + ) + + def get_winsdk_lib(): print("[build_module.py] Detect Windows SDK library directory") ret = "" @@ -80,12 +151,25 @@ def set_compiler_options(options): extra_compile_args.extend(["/EHsc"]) extra_link_args.extend(["/ignore:4217"]) + if LINUX or MAC: + # Compiler flags + if FAST_FLAG: + extra_compile_args.append("-O0") + else: + extra_compile_args.append("-O3") + + extra_compile_args.extend([ + "-DNDEBUG", + "-std=gnu++11", + ]) + if LINUX: - if len(sys.argv) > 1 and "--fast" in sys.argv: - sys.argv.remove("--fast") - # Fast mode disables optimization flags - print("[build_module.py] FAST mode On") - extra_compile_args.extend(["-flto", "-std=c++11"]) + os.environ["CC"] = "g++" + os.environ["CXX"] = "g++" + + if FAST_FLAG: + extra_compile_args.extend(["-flto", + "-std=gnu++11"]) extra_link_args.extend(["-flto"]) else: # Fix "ImportError ... undefined symbol ..." caused by CEF's @@ -93,15 +177,72 @@ def set_compiler_options(options): # Unfortunately -flto prolongs compilation time significantly. # More on the other flags: https://stackoverflow.com/questions/ # 6687630/ . - extra_compile_args.extend(["-flto", "-fdata-sections", - "-ffunction-sections", "-std=c++11"]) - extra_link_args.extend(["-flto", "-Wl,--gc-sections"]) + extra_compile_args.extend(["-flto", + "-fdata-sections", + "-ffunction-sections", + "-std=gnu++11"]) + extra_link_args.extend(["-flto", + "-Wl,--gc-sections"]) if MAC: - extra_compile_args.extend(["-std=c++11"]) - # extra_link_args.extend([]) - # os.environ["CC"] = "gcc" - # os.environ["CXX"] = "g++" + # Compiler environment variables + os.environ["CC"] = "c++" + os.environ["CXX"] = "c++" + + # COMPILER ARGS + + # -Wno-return-type-c-linkage to ignore: + # > warning: 'somefunc' has C-linkage specified, but returns + # > user-defined type 'sometype' which is incompatible with C + # + # -Wno-constant-logical-operand to ignore: + # > warning: use of logical '||' with constant operand + + extra_compile_args.extend([ + # Compile against libc++ otherwise error "symbol not found" + # with cef::logging::LogMessage symbol. Also include -lc++ + # and -lc++abi libraries. + "-stdlib=libc++", + "-Wno-return-type-c-linkage", + "-Wno-constant-logical-operand", + ]) + # From upstream CEF cefclient + extra_compile_args.extend([ + "-fno-strict-aliasing", + "-fno-rtti", + "-fno-threadsafe-statics", + "-fobjc-call-cxx-cdtors", + # Visibility of symbols: + "-fvisibility=hidden", + "-fvisibility-inlines-hidden", + ]) + # Visibility of symbols + extra_compile_args.extend([ + # "-flto", + # "-fdata-sections", + # "-ffunction-sections", + ]) + + # Build libcef_dll_wrapper: + # cmake -G "Ninja" -DPROJECT_ARCH="x86_64" + # -DCMAKE_CXX_FLAGS="-stdlib=libc++" + # -DCMAKE_BUILD_TYPE=Release .. + # ninja libcef_dll_wrapper + + # LINKER ARGS + extra_link_args.extend([ + "-mmacosx-version-min=10.7", + "-Wl,-search_paths_first", + "-F"+os.path.join(CEF_BINARIES_LIBRARIES, "bin"), + "-framework", "Chromium Embedded Framework", + "-Wl,-rpath,@loader_path", + ]) + if not FAST_FLAG: + extra_link_args.extend([ + # "-force_flat_namespace", + # "-flto", + "-Wl,-dead_strip", + ]) options["extra_compile_args"] = extra_compile_args options["extra_link_args"] = extra_link_args @@ -189,6 +330,8 @@ def get_library_dirs(): "Release_{os}" .format(os=OS_POSTFIX2)) ]) + if MAC: + library_dirs.append(os.path.join(CEF_BINARIES_LIBRARIES, "bin")) if MAC or LINUX: library_dirs.extend([ os.path.join(SRC_DIR, "client_handler"), @@ -215,10 +358,12 @@ def get_libraries(): ]) elif MAC: libraries.extend([ - 'client_handler', - 'cef_dll_wrapper', - 'cefpythonapp', - 'cpp_utils' + "c++", + "c++abi", + "cef_dll_wrapper", + "cefpythonapp", + "client_handler", + "cpp_utils", ]) elif LINUX: libraries.extend([ @@ -237,8 +382,8 @@ def get_libraries(): def get_ext_modules(options): ext_modules = [Extension( - "cefpython_py%s" % PYVERSION, - ["cefpython.pyx"], + name=MODULE_NAME_NOEXT, + sources=["cefpython.pyx"], # Ignore the warning in the console: # > C:\Python27\lib\distutils\extension.py:133: UserWarning: @@ -282,24 +427,5 @@ def compile_time_constants(): fd.write('DEF PY_MAJOR_VERSION = %s\n' % sys.version_info.major) -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() - set_compiler_options(options) - options["include_dirs"] = get_include_dirs() - options["library_dirs"] = get_library_dirs() - options["libraries"] = get_libraries() - print("[build_module.py] Execute setup()") - setup( - name='cefpython_py%s' % PYVERSION, - cmdclass={'build_ext': build_ext}, - ext_modules=get_ext_modules(options) - ) - - if __name__ == "__main__": main() diff --git a/tools/common.py b/tools/common.py index 9a5c5b61..bb161553 100644 --- a/tools/common.py +++ b/tools/common.py @@ -29,6 +29,18 @@ # Python version eg. 27 PYVERSION = str(sys.version_info[0])+str(sys.version_info[1]) +# Module extension +if WINDOWS: + MODULE_EXT = "pyd" +else: + MODULE_EXT = "so" + +# CEF Python module name +MODULE_NAME_TEMPLATE = "cefpython_py{pyversion}.{ext}" +MODULE_NAME_TEMPLATE_NOEXT = "cefpython_py{pyversion}" +MODULE_NAME = MODULE_NAME_TEMPLATE.format(pyversion=PYVERSION, ext=MODULE_EXT) +MODULE_NAME_NOEXT = MODULE_NAME_TEMPLATE_NOEXT.format(pyversion=PYVERSION) + # Root directory assert __file__ ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) diff --git a/tools/installer/cefpython3.__init__.py b/tools/installer/cefpython3.__init__.py index 67ce48ba..3d821b89 100644 --- a/tools/installer/cefpython3.__init__.py +++ b/tools/installer/cefpython3.__init__.py @@ -26,42 +26,25 @@ 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. os.environ["LD_LIBRARY_PATH"] = package_dir -# On Mac it works without setting library paths. Better not set it, -# as maybe user's app will set it itself. -# > os.environ["DYLD_LIBRARY_PATH"] = package_dir -# > os.environ["DYLD_FRAMEWORK_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. +# Loading library dynamically using ctypes.CDLL is required on Linux. +# TODO: Check if on Linux libcef.so can be linked like on Mac. +# On Mac the CEF framework dependency information is added to +# the cefpython*.so module by linking to CEF framework. # 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": - cef_framework = "Chromium Embedded Framework.framework" - libcef_name = "Chromium Embedded Framework" - # Search for it in current directory or in ../Frameworks/ dir - # in case this is user's app packaged for distribution. - libcef1 = os.path.join(package_dir, cef_framework, libcef_name) - libcef2 = os.path.join(package_dir, "..", "Frameworks", cef_framework, - libcef_name) - if os.path.exists(libcef1): - libcef = libcef1 - elif os.path.exists(libcef2): - libcef = libcef2 - else: - raise Exception("Can't find: " + cef_framework) -elif platform.system() == "Linux": +if 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 sys.version_info[:2] == (2, 8): +if sys.version_info[:2] == (2, 7): # noinspection PyUnresolvedReferences from . import cefpython_py27 as cefpython elif sys.version_info[:2] == (3, 4):