From 5d78d333efdebd4ab221a8cbd94a8d78408ebf85 Mon Sep 17 00:00:00 2001 From: peterq Date: Thu, 21 Mar 2019 22:29:16 +0800 Subject: [PATCH] =?UTF-8?q?init:=E3=80=80=E6=90=AD=E5=BB=BAqt5=20=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 + LICENSE | 165 +++ README.md | 2 + go.mod | 1 + pan-light.go | 128 ++ pan-light.pro | 32 + pc/go.mod | 10 + pc/go.sum | 32 + pc/gui/bridge/api_for_qml.go | 35 + pc/gui/bridge/router.go | 120 ++ pc/gui/comp/BridgeComp.go | 69 + pc/gui/mod.go | 1 + pc/gui/qml/comps/bridge.qml | 16 + pc/gui/qml/comps/timer.qml | 13 + pc/gui/qml/js/global.js | 30 + pc/gui/qml/js/promise.js | 300 +++++ pc/gui/qml/js/util.js | 114 ++ pc/gui/qml/main.qml | 33 + pc/gui/qml/qml.qrc | 10 + pc/gui/qt-rpc/rpc.go | 36 + pc/pan-light-pc.go | 35 + qt/.gitignore | 24 + qt/LICENSE | 165 +++ qt/README.md | 81 ++ qt/cmd/env/darwin_amd64.go | 5 + qt/cmd/env/linux_amd64.go | 5 + qt/cmd/env/windows_amd64.go | 5 + qt/cmd/qtdeploy/main.go | 137 ++ qt/cmd/qtminimal/main.go | 104 ++ qt/cmd/qtmoc/main.go | 110 ++ qt/cmd/qtrcc/main.go | 113 ++ qt/cmd/qtsetup/main.go | 120 ++ qt/go.mod | 22 + qt/go.sum | 50 + qt/qt.go | 190 +++ qt/qt_android.go | 92 ++ qt/qt_js.go | 29 + qt/qt_notjs.go | 11 + qt/qt_wasm.go | 24 + .../binding/converter/body_input_cpp.go | 221 ++++ .../binding/converter/body_input_go.go | 184 +++ .../binding/converter/body_output_cpp.go | 110 ++ .../binding/converter/body_output_go.go | 24 + qt/tool-chain/binding/converter/enum.go | 45 + qt/tool-chain/binding/converter/header.go | 352 +++++ qt/tool-chain/binding/converter/helper.go | 304 +++++ qt/tool-chain/binding/converter/input.go | 693 ++++++++++ qt/tool-chain/binding/converter/output.go | 1156 +++++++++++++++++ qt/tool-chain/binding/converter/type.go | 567 ++++++++ .../files/utils-androidextras_android.go | 70 + qt/tool-chain/binding/parser/class.go | 365 ++++++ qt/tool-chain/binding/parser/class_add.go | 259 ++++ qt/tool-chain/binding/parser/class_fix.go | 475 +++++++ qt/tool-chain/binding/parser/class_remove.go | 159 +++ qt/tool-chain/binding/parser/enum.go | 42 + qt/tool-chain/binding/parser/function.go | 738 +++++++++++ qt/tool-chain/binding/parser/function_fix.go | 696 ++++++++++ qt/tool-chain/binding/parser/helper.go | 596 +++++++++ qt/tool-chain/binding/parser/module.go | 106 ++ qt/tool-chain/binding/parser/module_add.go | 84 ++ qt/tool-chain/binding/parser/module_remove.go | 16 + qt/tool-chain/binding/parser/parser.go | 123 ++ qt/tool-chain/binding/parser/sailfish.go | 94 ++ qt/tool-chain/binding/parser/variable.go | 193 +++ qt/tool-chain/binding/templater/enum_cpp.go | 46 + qt/tool-chain/binding/templater/enum_go.go | 53 + .../binding/templater/function_cpp.go | 691 ++++++++++ .../binding/templater/function_go.go | 661 ++++++++++ qt/tool-chain/binding/templater/helper.go | 74 ++ qt/tool-chain/binding/templater/template_c.go | 243 ++++ .../binding/templater/template_cgo.go | 244 ++++ .../binding/templater/template_cgo_qmake.go | 707 ++++++++++ .../binding/templater/template_cpp.go | 811 ++++++++++++ .../binding/templater/template_go.go | 804 ++++++++++++ qt/tool-chain/binding/templater/template_h.go | 105 ++ qt/tool-chain/binding/templater/templater.go | 76 ++ qt/tool-chain/ci/darwin.sh | 68 + qt/tool-chain/ci/iscript.qs | 114 ++ qt/tool-chain/ci/linux.sh | 94 ++ qt/tool-chain/cmd/cmd.go | 781 +++++++++++ qt/tool-chain/cmd/deploy/assets.go | 785 +++++++++++ qt/tool-chain/cmd/deploy/build.go | 211 +++ qt/tool-chain/cmd/deploy/build_escape.go | 38 + qt/tool-chain/cmd/deploy/build_test.go | 74 ++ qt/tool-chain/cmd/deploy/bundle.go | 743 +++++++++++ qt/tool-chain/cmd/deploy/deploy.go | 98 ++ qt/tool-chain/cmd/deploy/run.go | 80 ++ qt/tool-chain/cmd/minimal/minimal.go | 392 ++++++ qt/tool-chain/cmd/setup/check.go | 153 +++ qt/tool-chain/cmd/setup/generate.go | 69 + qt/tool-chain/cmd/setup/install.go | 137 ++ qt/tool-chain/cmd/setup/prep.go | 64 + qt/tool-chain/cmd/setup/test.go | 163 +++ qt/tool-chain/cmd/setup/update.go | 58 + qt/tool-chain/cmd/tools.go | 7 + qt/tool-chain/cmd/utils.go | 165 +++ qt/tool-chain/docker/android/Dockerfile | 91 ++ qt/tool-chain/docker/docker_job_template.yml | 9 + .../docker/docker_pipelines_template.yml | 193 +++ qt/tool-chain/docker/js/Dockerfile | 32 + qt/tool-chain/docker/js/Dockerfile.base | 17 + qt/tool-chain/docker/js/Dockerfile.wasm | 38 + qt/tool-chain/docker/linux/Dockerfile | 42 + qt/tool-chain/docker/linux/Dockerfile.56 | 40 + qt/tool-chain/docker/linux/Dockerfile.59 | 40 + qt/tool-chain/docker/linux/Dockerfile.arch | 32 + .../docker/linux/Dockerfile.ubuntu_16_04 | 31 + .../docker/linux/Dockerfile.ubuntu_18_04 | 31 + qt/tool-chain/docker/rpi/Dockerfile.base | 27 + qt/tool-chain/docker/rpi/Dockerfile.rpi1 | 10 + qt/tool-chain/docker/rpi/Dockerfile.rpi2 | 10 + qt/tool-chain/docker/rpi/Dockerfile.rpi3 | 10 + qt/tool-chain/docker/sailfish/Dockerfile | 81 ++ .../docker/ubports/Dockerfile.64_vivid | 24 + .../docker/ubports/Dockerfile.64_xenial | 22 + .../docker/ubports/Dockerfile.arm_vivid | 27 + .../docker/ubports/Dockerfile.arm_xenial | 24 + .../docker/windows_32_shared/Dockerfile | 24 + .../docker/windows_32_shared/Dockerfile.base | 8 + .../docker/windows_32_static/Dockerfile | 24 + .../docker/windows_32_static/Dockerfile.base | 11 + .../docker/windows_64_shared/Dockerfile | 24 + .../docker/windows_64_shared/Dockerfile.base | 8 + .../docker/windows_64_static/Dockerfile | 24 + .../docker/windows_64_static/Dockerfile.base | 11 + .../windows_legacy/Dockerfile.32_shared | 30 + .../windows_legacy/Dockerfile.32_static | 30 + .../windows_legacy/Dockerfile.64_shared | 30 + .../windows_legacy/Dockerfile.64_static | 30 + qt/tool-chain/docker/wine/Dockerfile | 52 + .../docker/wine/Dockerfile.32_shared | 105 ++ .../docker/wine/Dockerfile.32_static | 130 ++ qt/tool-chain/docker/wine/Dockerfile.56 | 51 + qt/tool-chain/docker/wine/Dockerfile.56_xp | 51 + qt/tool-chain/docker/wine/Dockerfile.59 | 51 + .../docker/wine/Dockerfile.64_shared | 105 ++ .../docker/wine/Dockerfile.64_static | 131 ++ qt/tool-chain/docker/wine/Dockerfile.base | 24 + qt/tool-chain/docker/wine/Dockerfile.base_xp | 24 + qt/tool-chain/docker/wine/rspfile.patch | 133 ++ qt/tool-chain/utils/android.go | 58 + qt/tool-chain/utils/darwin.go | 150 +++ qt/tool-chain/utils/env.go | 307 +++++ qt/tool-chain/utils/gopath.go | 53 + qt/tool-chain/utils/linux.go | 124 ++ qt/tool-chain/utils/logger.go | 18 + qt/tool-chain/utils/rpi.go | 45 + qt/tool-chain/utils/sailfish.go | 50 + qt/tool-chain/utils/utils.go | 157 +++ qt/tool-chain/utils/walk.go | 115 ++ qt/tool-chain/utils/walk_test.go | 155 +++ qt/tool-chain/utils/windows.go | 71 + qt/tool-chain/vagrant/darwin/Vagrantfile | 46 + qt/tool-chain/vagrant/docker/Vagrantfile | 98 ++ qt/tool-chain/vagrant/linux/Vagrantfile | 64 + qt/tool-chain/vagrant/past.sh | 10 + qt/tool-chain/vagrant/pre.bat | 175 +++ qt/tool-chain/vagrant/pre.sh | 65 + qt/tool-chain/vagrant/windows/Vagrantfile | 48 + 159 files changed, 22707 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 go.mod create mode 100644 pan-light.go create mode 100644 pan-light.pro create mode 100644 pc/go.mod create mode 100644 pc/go.sum create mode 100644 pc/gui/bridge/api_for_qml.go create mode 100644 pc/gui/bridge/router.go create mode 100644 pc/gui/comp/BridgeComp.go create mode 100644 pc/gui/mod.go create mode 100644 pc/gui/qml/comps/bridge.qml create mode 100644 pc/gui/qml/comps/timer.qml create mode 100644 pc/gui/qml/js/global.js create mode 100644 pc/gui/qml/js/promise.js create mode 100644 pc/gui/qml/js/util.js create mode 100644 pc/gui/qml/main.qml create mode 100644 pc/gui/qml/qml.qrc create mode 100644 pc/gui/qt-rpc/rpc.go create mode 100644 pc/pan-light-pc.go create mode 100644 qt/.gitignore create mode 100644 qt/LICENSE create mode 100644 qt/README.md create mode 100644 qt/cmd/env/darwin_amd64.go create mode 100644 qt/cmd/env/linux_amd64.go create mode 100644 qt/cmd/env/windows_amd64.go create mode 100644 qt/cmd/qtdeploy/main.go create mode 100644 qt/cmd/qtminimal/main.go create mode 100644 qt/cmd/qtmoc/main.go create mode 100644 qt/cmd/qtrcc/main.go create mode 100644 qt/cmd/qtsetup/main.go create mode 100644 qt/go.mod create mode 100644 qt/go.sum create mode 100644 qt/qt.go create mode 100644 qt/qt_android.go create mode 100644 qt/qt_js.go create mode 100644 qt/qt_notjs.go create mode 100644 qt/qt_wasm.go create mode 100644 qt/tool-chain/binding/converter/body_input_cpp.go create mode 100644 qt/tool-chain/binding/converter/body_input_go.go create mode 100644 qt/tool-chain/binding/converter/body_output_cpp.go create mode 100644 qt/tool-chain/binding/converter/body_output_go.go create mode 100644 qt/tool-chain/binding/converter/enum.go create mode 100644 qt/tool-chain/binding/converter/header.go create mode 100644 qt/tool-chain/binding/converter/helper.go create mode 100644 qt/tool-chain/binding/converter/input.go create mode 100644 qt/tool-chain/binding/converter/output.go create mode 100644 qt/tool-chain/binding/converter/type.go create mode 100644 qt/tool-chain/binding/files/utils-androidextras_android.go create mode 100644 qt/tool-chain/binding/parser/class.go create mode 100644 qt/tool-chain/binding/parser/class_add.go create mode 100644 qt/tool-chain/binding/parser/class_fix.go create mode 100644 qt/tool-chain/binding/parser/class_remove.go create mode 100644 qt/tool-chain/binding/parser/enum.go create mode 100644 qt/tool-chain/binding/parser/function.go create mode 100644 qt/tool-chain/binding/parser/function_fix.go create mode 100644 qt/tool-chain/binding/parser/helper.go create mode 100644 qt/tool-chain/binding/parser/module.go create mode 100644 qt/tool-chain/binding/parser/module_add.go create mode 100644 qt/tool-chain/binding/parser/module_remove.go create mode 100644 qt/tool-chain/binding/parser/parser.go create mode 100644 qt/tool-chain/binding/parser/sailfish.go create mode 100644 qt/tool-chain/binding/parser/variable.go create mode 100644 qt/tool-chain/binding/templater/enum_cpp.go create mode 100644 qt/tool-chain/binding/templater/enum_go.go create mode 100644 qt/tool-chain/binding/templater/function_cpp.go create mode 100644 qt/tool-chain/binding/templater/function_go.go create mode 100644 qt/tool-chain/binding/templater/helper.go create mode 100644 qt/tool-chain/binding/templater/template_c.go create mode 100644 qt/tool-chain/binding/templater/template_cgo.go create mode 100644 qt/tool-chain/binding/templater/template_cgo_qmake.go create mode 100644 qt/tool-chain/binding/templater/template_cpp.go create mode 100644 qt/tool-chain/binding/templater/template_go.go create mode 100644 qt/tool-chain/binding/templater/template_h.go create mode 100644 qt/tool-chain/binding/templater/templater.go create mode 100755 qt/tool-chain/ci/darwin.sh create mode 100644 qt/tool-chain/ci/iscript.qs create mode 100755 qt/tool-chain/ci/linux.sh create mode 100644 qt/tool-chain/cmd/cmd.go create mode 100644 qt/tool-chain/cmd/deploy/assets.go create mode 100644 qt/tool-chain/cmd/deploy/build.go create mode 100644 qt/tool-chain/cmd/deploy/build_escape.go create mode 100644 qt/tool-chain/cmd/deploy/build_test.go create mode 100644 qt/tool-chain/cmd/deploy/bundle.go create mode 100644 qt/tool-chain/cmd/deploy/deploy.go create mode 100644 qt/tool-chain/cmd/deploy/run.go create mode 100644 qt/tool-chain/cmd/minimal/minimal.go create mode 100644 qt/tool-chain/cmd/setup/check.go create mode 100644 qt/tool-chain/cmd/setup/generate.go create mode 100644 qt/tool-chain/cmd/setup/install.go create mode 100644 qt/tool-chain/cmd/setup/prep.go create mode 100644 qt/tool-chain/cmd/setup/test.go create mode 100644 qt/tool-chain/cmd/setup/update.go create mode 100644 qt/tool-chain/cmd/tools.go create mode 100644 qt/tool-chain/cmd/utils.go create mode 100644 qt/tool-chain/docker/android/Dockerfile create mode 100644 qt/tool-chain/docker/docker_job_template.yml create mode 100644 qt/tool-chain/docker/docker_pipelines_template.yml create mode 100644 qt/tool-chain/docker/js/Dockerfile create mode 100644 qt/tool-chain/docker/js/Dockerfile.base create mode 100644 qt/tool-chain/docker/js/Dockerfile.wasm create mode 100644 qt/tool-chain/docker/linux/Dockerfile create mode 100644 qt/tool-chain/docker/linux/Dockerfile.56 create mode 100644 qt/tool-chain/docker/linux/Dockerfile.59 create mode 100644 qt/tool-chain/docker/linux/Dockerfile.arch create mode 100644 qt/tool-chain/docker/linux/Dockerfile.ubuntu_16_04 create mode 100644 qt/tool-chain/docker/linux/Dockerfile.ubuntu_18_04 create mode 100644 qt/tool-chain/docker/rpi/Dockerfile.base create mode 100644 qt/tool-chain/docker/rpi/Dockerfile.rpi1 create mode 100644 qt/tool-chain/docker/rpi/Dockerfile.rpi2 create mode 100644 qt/tool-chain/docker/rpi/Dockerfile.rpi3 create mode 100644 qt/tool-chain/docker/sailfish/Dockerfile create mode 100644 qt/tool-chain/docker/ubports/Dockerfile.64_vivid create mode 100644 qt/tool-chain/docker/ubports/Dockerfile.64_xenial create mode 100644 qt/tool-chain/docker/ubports/Dockerfile.arm_vivid create mode 100644 qt/tool-chain/docker/ubports/Dockerfile.arm_xenial create mode 100644 qt/tool-chain/docker/windows_32_shared/Dockerfile create mode 100644 qt/tool-chain/docker/windows_32_shared/Dockerfile.base create mode 100644 qt/tool-chain/docker/windows_32_static/Dockerfile create mode 100644 qt/tool-chain/docker/windows_32_static/Dockerfile.base create mode 100644 qt/tool-chain/docker/windows_64_shared/Dockerfile create mode 100644 qt/tool-chain/docker/windows_64_shared/Dockerfile.base create mode 100644 qt/tool-chain/docker/windows_64_static/Dockerfile create mode 100644 qt/tool-chain/docker/windows_64_static/Dockerfile.base create mode 100644 qt/tool-chain/docker/windows_legacy/Dockerfile.32_shared create mode 100644 qt/tool-chain/docker/windows_legacy/Dockerfile.32_static create mode 100644 qt/tool-chain/docker/windows_legacy/Dockerfile.64_shared create mode 100644 qt/tool-chain/docker/windows_legacy/Dockerfile.64_static create mode 100644 qt/tool-chain/docker/wine/Dockerfile create mode 100644 qt/tool-chain/docker/wine/Dockerfile.32_shared create mode 100644 qt/tool-chain/docker/wine/Dockerfile.32_static create mode 100644 qt/tool-chain/docker/wine/Dockerfile.56 create mode 100644 qt/tool-chain/docker/wine/Dockerfile.56_xp create mode 100644 qt/tool-chain/docker/wine/Dockerfile.59 create mode 100644 qt/tool-chain/docker/wine/Dockerfile.64_shared create mode 100644 qt/tool-chain/docker/wine/Dockerfile.64_static create mode 100644 qt/tool-chain/docker/wine/Dockerfile.base create mode 100644 qt/tool-chain/docker/wine/Dockerfile.base_xp create mode 100644 qt/tool-chain/docker/wine/rspfile.patch create mode 100644 qt/tool-chain/utils/android.go create mode 100644 qt/tool-chain/utils/darwin.go create mode 100644 qt/tool-chain/utils/env.go create mode 100644 qt/tool-chain/utils/gopath.go create mode 100644 qt/tool-chain/utils/linux.go create mode 100644 qt/tool-chain/utils/logger.go create mode 100644 qt/tool-chain/utils/rpi.go create mode 100644 qt/tool-chain/utils/sailfish.go create mode 100644 qt/tool-chain/utils/utils.go create mode 100644 qt/tool-chain/utils/walk.go create mode 100644 qt/tool-chain/utils/walk_test.go create mode 100644 qt/tool-chain/utils/windows.go create mode 100644 qt/tool-chain/vagrant/darwin/Vagrantfile create mode 100644 qt/tool-chain/vagrant/docker/Vagrantfile create mode 100644 qt/tool-chain/vagrant/linux/Vagrantfile create mode 100755 qt/tool-chain/vagrant/past.sh create mode 100644 qt/tool-chain/vagrant/pre.bat create mode 100755 qt/tool-chain/vagrant/pre.sh create mode 100644 qt/tool-chain/vagrant/windows/Vagrantfile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce4243a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.pro.user +.idea +pc/vendor +moc* +*.autosave +rcc* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bfe1f97 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +## pan-light, 百度网盘不限速客户端 + diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e5cc197 --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module github.com/peterq/pan-light diff --git a/pan-light.go b/pan-light.go new file mode 100644 index 0000000..e98a313 --- /dev/null +++ b/pan-light.go @@ -0,0 +1,128 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" +) + +func main() { + flag.Usage = func() { + println("用法: pan-light [cmd] [sub cmd]\n") + + println("参数:\n") + flag.PrintDefaults() + println() + + println("命令:\n") + for _, m := range []struct{ name, desc string }{ + {"pc start", "启动pc客户端"}, + {"pc moc", "生成moc"}, + } { + fmt.Printf(" %v%v%v\n", m.name, strings.Repeat(" ", 12-len(m.name)), m.desc) + } + println() + + os.Exit(0) + } + flag.Parse() + + cmd := "pc" + + cmd = flag.Arg(0) + + switch cmd { + case "pc": + pcCmd() + default: + flag.Usage() + } +} + +func pcCmd() { + cmd := flag.Arg(1) + switch cmd { + case "start": + pcStart() + case "moc": + pcMoc() + default: + flag.Usage() + } +} + +func qtBin(name string) string { + v, ok1 := os.LookupEnv("QT_VERSION") + d, ok2 := os.LookupEnv("QT_DIR") + if !ok1 || !ok2 { + panic("请先配置qt环境变量") + } + return d + "/" + v + "/gcc_64/bin/" + name +} + +func pcStart() { + // 打包qml + log.Println("打包qml...") + c := cmd("go", "run", "../qt/cmd/qtrcc/main.go", "desktop", "gui/qml") + c.Dir, _ = filepath.Abs("./pc") + c.Run() + + // 启动 + log.Println("启动pc客户端") + c = cmd("go", "run", "pan-light-pc.go") + c.Dir = "./pc" + c.Run() +} + +// 清除svg的style font节点, 减小尺寸 +func iconSimplify() { + dir := "./pc/gui/qml/assets/images/icons" + ls, err := ioutil.ReadDir(dir) + if err != nil { + panic(err) + } + reg := regexp.MustCompile(`\(.|\n)*\`) + for _, f := range ls { + if strings.Index(f.Name(), ".svg") == len(f.Name())-4 { + filename := dir + "/" + f.Name() + bin, err := ioutil.ReadFile(filename) + if err != nil { + panic(err) + } + content := string(bin) + target := reg.ReplaceAllString(content, "") + if target != content { + err = ioutil.WriteFile(filename, []byte(target), os.ModePerm) + if err != nil { + panic(err) + } + } + } + } +} + +func pcMoc() { + log.Println("moc...") + c := cmd("go", "run", "../qt/cmd/qtmoc/main.go", "desktop", "gui/comp") + c.Dir, _ = filepath.Abs("./pc") + e := c.Run() + if e != nil { + log.Println(e) + } +} + +func cmd(name string, arg ...string) *exec.Cmd { + cmd := exec.Command(name, arg...) + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + return cmd +} diff --git a/pan-light.pro b/pan-light.pro new file mode 100644 index 0000000..a7bd644 --- /dev/null +++ b/pan-light.pro @@ -0,0 +1,32 @@ +QT += quick +CONFIG += c++11 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Refer to the documentation for the +# deprecated API to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += + +RESOURCES += pc/gui/qml/qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += + + diff --git a/pc/go.mod b/pc/go.mod new file mode 100644 index 0000000..c8297f0 --- /dev/null +++ b/pc/go.mod @@ -0,0 +1,10 @@ +module github.com/peterq/pan-light/pc + +require ( + github.com/peterq/pan-light/qt v0.0.0 + github.com/peterq/pan-light/qt/bindings v0.0.0 +) + +replace github.com/peterq/pan-light/qt/bindings => ../qt/bindings + +replace github.com/peterq/pan-light/qt => ../qt diff --git a/pc/go.sum b/pc/go.sum new file mode 100644 index 0000000..306b4a7 --- /dev/null +++ b/pc/go.sum @@ -0,0 +1,32 @@ +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shurcooL/go v0.0.0-20181215222900-0143a8f55f04/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/therecipe/env_darwin_amd64_512 v0.0.0-20190102210040-529084f510b3/go.mod h1:4O+Hy+lamRLGBSpC6us4ri+GigPeYdeG9wQRZFH3Rkc= +github.com/therecipe/env_linux_amd64_512 v0.0.0-20190102180622-9b3faedb5806/go.mod h1:rUlYPwVtCiv2SKco9iedVR3TouC3WvEzagEWtL2Mim8= +github.com/therecipe/env_windows_amd64_512 v0.0.0-20190121195954-78ba6026af9b/go.mod h1:hcI7eNXyJoOLCMhct/6aNYySZ4s8pXEejlkSF0tdlww= +github.com/therecipe/env_windows_amd64_512/Tools v0.0.0-20190121195954-78ba6026af9b/go.mod h1:uo9CZHpTlY/tCnFZXyhV/weqZGYWsbYmPOU8UANqU2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190319232107-3f1ed9edd1b4 h1:4oAPsdy/MJIeaCzEMEhYwYBU/gHkXH52Xa4M+0GBHfA= +golang.org/x/tools v0.0.0-20190319232107-3f1ed9edd1b4/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/pc/gui/bridge/api_for_qml.go b/pc/gui/bridge/api_for_qml.go new file mode 100644 index 0000000..d0f9f23 --- /dev/null +++ b/pc/gui/bridge/api_for_qml.go @@ -0,0 +1,35 @@ +package bridge + +import ( + "time" +) + +var callSyncMap = map[string]func(map[string]interface{}) interface{}{ + // 通知异步任务 + "asyncTaskMsg": func(p map[string]interface{}) interface{} { + AsyncTaskChanMapLock.RLock() + ch, ok := AsyncTaskChanMap[p["asyncCallId"].(string)] + AsyncTaskChanMapLock.RUnlock() + if !ok { + return false + } + ch <- p["msg"] + return true + }, + // 获取当前时间 + "time": func(p map[string]interface{}) interface{} { + return time.Now().UnixNano() + }, +} + +var callAsyncMap = map[string]func(map[string]interface{}, + func(interface{}), func(interface{}), func(interface{}), chan interface{}){ + + "wait": func(p map[string]interface{}, resolve func(interface{}), reject func(interface{}), progress func(interface{}), qmlMsg chan interface{}) { + for i := 0; i < int(p["time"].(float64)); i++ { + time.Sleep(time.Second) + progress("current time is " + time.Now().String()) + } + resolve("wait complete") + }, +} diff --git a/pc/gui/bridge/router.go b/pc/gui/bridge/router.go new file mode 100644 index 0000000..b433a79 --- /dev/null +++ b/pc/gui/bridge/router.go @@ -0,0 +1,120 @@ +package bridge + +import ( + "context" + "fmt" + "github.com/peterq/pan-light/pc/gui/qt-rpc" + "log" + "runtime/debug" + "sync" + "time" +) + +// 业务逻辑侧衔接ui + +var sendDataToQml func(string) +var OnClose func(func()) +var UiContext = context.WithValue(context.Background(), "start_time", time.Now()) + +type tJson map[string]interface{} + +func NotifyQml(event string, data map[string]interface{}) { + qt_rpc.NotifyQml(event, (*qt_rpc.Gson)(&data)) +} + +var AsyncTaskChanMap = make(map[string]chan interface{}) +var AsyncTaskChanMapLock = new(sync.RWMutex) + +func init() { + qt_rpc.CallGoAsync = callGoAsync + qt_rpc.CallGoSync = callGoSync +} + +func callGoAsync(data *qt_rpc.Gson) { + var err error + p := *data + action := p["action"].(string) + if fn, ok := callAsyncMap[action]; ok { + go func() { + finish := func() {} + defer func() { + if e := recover(); e != nil { + err = e.(error) + log.Println("调用go api函数出错"+action, e) + log.Printf("stack %s", debug.Stack()) + NotifyQml("call.ret", map[string]interface{}{ + "type": "reject", + "callId": p["callId"], + "reject": err.Error(), + }) + finish() + } + }() + var qmlMsg chan interface{} + if withCh, ok := p["chan"]; ok && withCh.(bool) { + qmlMsg = make(chan interface{}) + AsyncTaskChanMapLock.Lock() + AsyncTaskChanMap[p["callId"].(string)] = qmlMsg + AsyncTaskChanMapLock.Unlock() + finish = func() { + AsyncTaskChanMapLock.Lock() + defer AsyncTaskChanMapLock.Unlock() + close(qmlMsg) + delete(AsyncTaskChanMap, p["callId"].(string)) + } + } + fn(p["param"].(map[string]interface{}), func(i interface{}) { + NotifyQml("call.ret", map[string]interface{}{ + "type": "resolve", + "callId": p["callId"], + "resolve": i, + }) + finish() + }, func(i interface{}) { + NotifyQml("call.ret", map[string]interface{}{ + "type": "reject", + "callId": p["callId"], + "reject": i, + }) + finish() + }, func(i interface{}) { + NotifyQml("call.ret", map[string]interface{}{ + "type": "progress", + "callId": p["callId"], + "progress": i, + }) + }, qmlMsg) + }() + } else { + NotifyQml("call.ret", map[string]interface{}{ + "type": "reject", + "callId": p["callId"], + "reject": fmt.Sprintf("api [%s] not exist", action), + }) + } +} + +func callGoSync(data *qt_rpc.Gson) (retPointer *qt_rpc.Gson) { + ret := qt_rpc.Gson{} + defer func() { retPointer = &ret }() + p := *data + var err error + action := p["action"].(string) + if fn, ok := callSyncMap[action]; ok { + defer func() { + if e := recover(); e != nil { + err = e.(error) + log.Println("调用go api函数出错"+action, e) + log.Printf("stack %s", debug.Stack()) + ret["error"] = "call go panic" + return + } + }() + result := fn(p["param"].(map[string]interface{})) + ret["result"] = result + return + } else { + ret["error"] = "api not exist" + return + } +} diff --git a/pc/gui/comp/BridgeComp.go b/pc/gui/comp/BridgeComp.go new file mode 100644 index 0000000..d0a7658 --- /dev/null +++ b/pc/gui/comp/BridgeComp.go @@ -0,0 +1,69 @@ +package comp + +import ( + "encoding/json" + "github.com/peterq/pan-light/pc/gui/qt-rpc" + "github.com/peterq/pan-light/qt/bindings/core" + "github.com/peterq/pan-light/qt/bindings/quick" + "github.com/peterq/pan-light/qt/bindings/widgets" + "log" +) + +func init() { + BridgeComp_QmlRegisterType2("PanLight", 1, 0, "BridgeComp") +} + +// 用来和qml通信的组件 +type BridgeComp struct { + quick.QQuickItem + + _ string `property:"someString"` + _ func() `constructor:"init"` + + _ func(w *quick.QQuickItem, attr core.Qt__WidgetAttribute, b bool) `signal:"changeAttribute,auto"` + _ func(msg string) `signal:"logMsg,auto"` + _ func(data interface{}) `signal:"test,auto"` + _ func(data string) string `slot:"callSync,auto"` + _ func(data string) `signal:"callAsync,auto"` + _ func(data string) `signal:"goMessage"` + //... +} + +var Bridge *BridgeComp + +func (t *BridgeComp) init() { + Bridge = t + qt_rpc.NotifyQml = func(event string, data *qt_rpc.Gson) { + (*data)["event"] = event + bin, _ := json.Marshal(*data) + t.GoMessage(string(bin)) + } +} +func (t *BridgeComp) changeAttribute(w *quick.QQuickItem, attr core.Qt__WidgetAttribute, b bool) { + widgets.NewQWidgetFromPointer(w.Pointer()).SetAttribute(attr, b) +} + +func (t *BridgeComp) logMsg(msg string) { + log.Println("bridge log:", msg) +} + +func (t *BridgeComp) test(data interface{}) { + //core.QVariant{} + //log.Println(fmt.Sprintf("%#v", data)) +} + +// 同步调用go +func (t *BridgeComp) callSync(data string) string { + var gson qt_rpc.Gson + json.Unmarshal([]byte(data), &gson) + result := qt_rpc.CallGoSync(&gson) + bin, _ := json.Marshal(*result) + return string(bin) +} + +// 异步调用go +func (t *BridgeComp) callAsync(data string) { + var gson qt_rpc.Gson + json.Unmarshal([]byte(data), &gson) + qt_rpc.CallGoAsync(&gson) +} diff --git a/pc/gui/mod.go b/pc/gui/mod.go new file mode 100644 index 0000000..194757a --- /dev/null +++ b/pc/gui/mod.go @@ -0,0 +1 @@ +package gui diff --git a/pc/gui/qml/comps/bridge.qml b/pc/gui/qml/comps/bridge.qml new file mode 100644 index 0000000..703c33f --- /dev/null +++ b/pc/gui/qml/comps/bridge.qml @@ -0,0 +1,16 @@ +import QtQuick 2.0 +import PanLight 1.0 + +BridgeComp { + id: root + + width: 250 + height: 450 + visible: false + someString: "ItemTemplateString" + + Component.onCompleted: { + root.logMsg('bridge ready') + root.test(function () {}) + } +} diff --git a/pc/gui/qml/comps/timer.qml b/pc/gui/qml/comps/timer.qml new file mode 100644 index 0000000..65a8fc7 --- /dev/null +++ b/pc/gui/qml/comps/timer.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Timer { + id: timer + property alias interval: timer.interval + property var cb + + running: true + onTriggered: { + cb() + timer.destroy() + } +} diff --git a/pc/gui/qml/js/global.js b/pc/gui/qml/js/global.js new file mode 100644 index 0000000..9076b47 --- /dev/null +++ b/pc/gui/qml/js/global.js @@ -0,0 +1,30 @@ +.pragma library +// 此文件存放全局变量和 Polly fill, 不要依赖其他js, 否则会造成依赖循环 + +// global +var g = {} +// qml 根组件 +var root + +function init(r) { + root = r +} + + +var setTimeout = (function () { + var timer = Qt.createComponent("../comps/timer.qml") + return function (cb, time) { + cb = cb || function () { + } + time = time || 0 + timer.createObject(null, { + interval: time, + cb: cb + }) + return function cancel() { + if (timer) { + timer.destroy() + } + } + } +})() diff --git a/pc/gui/qml/js/promise.js b/pc/gui/qml/js/promise.js new file mode 100644 index 0000000..301b3e5 --- /dev/null +++ b/pc/gui/qml/js/promise.js @@ -0,0 +1,300 @@ +// 实现了一个待progress回调的Promise +.pragma library + +.import "global.js" as G + +var setTimeout = G.setTimeout +var Promise = (function () { + 'use strict'; + + /** + * @this {Promise} + */ + function finallyConstructor(callback) { + var constructor = this.constructor; + return this.then( + function (value) { + return constructor.resolve(callback()).then(function () { + return value; + }); + }, + function (reason) { + return constructor.resolve(callback()).then(function () { + return constructor.reject(reason); + }); + } + ); + } + +// Store setTimeout reference so promise-polyfill will be unaffected by +// other code modifying setTimeout (like sinon.useFakeTimers()) + var setTimeoutFunc = setTimeout; + + function noop() { + } + +// Polyfill for Function.prototype.bind + function bind(fn, thisArg) { + return function () { + fn.apply(thisArg, arguments); + }; + } + + /** + * @constructor + * @param {Function} fn + */ + function Promise(fn) { + if (!(this instanceof Promise)) + throw new TypeError('Promises must be constructed via new'); + if (typeof fn !== 'function') throw new TypeError('not a function'); + /** @type {!number} */ + this._state = 0; + /** @type {!boolean} */ + this._handled = false; + /** @type {Promise|undefined} */ + this._value = undefined; + /** @type {!Array} */ + this._deferreds = []; + + doResolve(fn, this); + } + + function handle(self, deferred) { + while (self._state === 3) { + self = self._value; + } + if (self._state === 0) { + self._deferreds.push(deferred); + return; + } + self._handled = true; + Promise._immediateFn(function () { + var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; + if (cb === null) { + (self._state === 1 ? resolve : reject)(deferred.promise, self._value); + return; + } + var ret; + try { + ret = cb(self._value); + } catch (e) { + reject(deferred.promise, e); + return; + } + resolve(deferred.promise, ret); + }); + } + + function resolve(self, newValue) { + try { + // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure + if (newValue === self) + throw new TypeError('A promise cannot be resolved with itself.'); + if ( + newValue && + (typeof newValue === 'object' || typeof newValue === 'function') + ) { + var then = newValue.then; + if (newValue instanceof Promise) { + self._state = 3; + self._value = newValue; + finale(self); + return; + } else if (typeof then === 'function') { + doResolve(bind(then, newValue), self); + return; + } + } + self._state = 1; + self._value = newValue; + finale(self); + } catch (e) { + reject(self, e); + } + } + + function progress(self, newValue) { + try { + for (var i = 0, len = self._deferreds.length; i < len; i++) { + var deferred = self._deferreds[i] + var cb = deferred.onProgress + if (!cb) { + progress(deferred.promise, newValue); + continue + } + var ret + try { + ret = cb(newValue); + } catch (e) { + reject(deferred.promise, e); + return; + } + progress(deferred.promise, ret); + } + } catch (e) { + reject(self, e); + } + } + + function reject(self, newValue) { + self._state = 2; + self._value = newValue; + finale(self); + } + + function finale(self) { + if (self._state === 2 && self._deferreds.length === 0) { + Promise._immediateFn(function () { + if (!self._handled) { + Promise._unhandledRejectionFn(self._value); + } + }); + } + + for (var i = 0, len = self._deferreds.length; i < len; i++) { + handle(self, self._deferreds[i]); + } + self._deferreds = null; + } + + /** + * @constructor + */ + function Handler(onFulfilled, onRejected, onProgress, promise) { + this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null; + this.onRejected = typeof onRejected === 'function' ? onRejected : null; + this.onProgress = typeof onProgress === 'function' ? onProgress : null; + this.promise = promise; + } + + /** + * Take a potentially misbehaving resolver function and make sure + * onFulfilled and onRejected are only called once. + * + * Makes no guarantees about asynchrony. + */ + function doResolve(fn, self) { + var done = false; + try { + fn( + function (value) { + if (done) return; + done = true; + resolve(self, value); + }, + function (reason) { + if (done) return; + done = true; + reject(self, reason); + }, + function (p) { + if (done) return; + progress(self, p) + } + ); + } catch (ex) { + if (done) return; + done = true; + reject(self, ex); + } + } + + Promise.prototype['catch'] = function (onRejected) { + return this.then(null, onRejected); + }; + + Promise.prototype['progress'] = function (onProgeress) { + return this.then(null, null, onProgeress); + }; + + Promise.prototype.then = function (onFulfilled, onRejected, onProgress) { + // @ts-ignore + var prom = new this.constructor(noop); + prom.parent = this + handle(this, new Handler(onFulfilled, onRejected, onProgress, prom)); + return prom; + }; + + Promise.prototype['finally'] = finallyConstructor; + + Promise.all = function (arr) { + return new Promise(function (resolve, reject) { + if (!arr || typeof arr.length === 'undefined') + throw new TypeError('Promise.all accepts an array'); + var args = Array.prototype.slice.call(arr); + if (args.length === 0) return resolve([]); + var remaining = args.length; + + function res(i, val) { + try { + if (val && (typeof val === 'object' || typeof val === 'function')) { + var then = val.then; + if (typeof then === 'function') { + then.call( + val, + function (val) { + res(i, val); + }, + reject + ); + return; + } + } + args[i] = val; + if (--remaining === 0) { + resolve(args); + } + } catch (ex) { + reject(ex); + } + } + + for (var i = 0; i < args.length; i++) { + res(i, args[i]); + } + }); + }; + + Promise.resolve = function (value) { + if (value && typeof value === 'object' && value.constructor === Promise) { + return value; + } + + return new Promise(function (resolve) { + resolve(value); + }); + }; + + Promise.reject = function (value) { + return new Promise(function (resolve, reject) { + reject(value); + }); + }; + + Promise.race = function (values) { + return new Promise(function (resolve, reject) { + for (var i = 0, len = values.length; i < len; i++) { + values[i].then(resolve, reject); + } + }); + }; + +// Use polyfill for setImmediate for performance gains + Promise._immediateFn = + (typeof setImmediate === 'function' && + function (fn) { + setImmediate(fn); + }) || + function (fn) { + setTimeoutFunc(fn, 0); + }; + + Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) { + if (typeof console !== 'undefined' && console) { + console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console + } + }; + + return Promise +})() diff --git a/pc/gui/qml/js/util.js b/pc/gui/qml/js/util.js new file mode 100644 index 0000000..4d70e4d --- /dev/null +++ b/pc/gui/qml/js/util.js @@ -0,0 +1,114 @@ +.pragma library +.import QtQuick 2.0 as Q +.import "global.js" as G +.import "promise.js" as P +var event = {} +;(function () { + var map = {} + event.fire = function (evt, data) { + if (!map[evt]) return + map[evt].forEach(function (fn, idx) { + try { + fn(data) + if (fn.once) { + map[evt].splice(idx, 1) + } + } catch (e) { + console.error(evt, fn, e) + } + }) + } + + event.on = function (evt, fn) { + map[evt] = map[evt] || [] + map[evt].push(fn) + return function () { + var idx = map[evt].findIndex(function (v) { + return v === fn + }) + if (idx >= 0) { + map[evt].splice(idx, 1) + } + } + } + + event.once = function (evt, fn) { + fn.once = 1 + return event.on(evt, fn) + } + +})() + +var bridge = (function () { + var comp = loadComponent(function () { + }, "../comps/bridge.qml") + var ins = comp.createObject(G.root) + ins.goMessage.connect(function (data) { + var obj = JSON.parse(data) + event.fire('go.' + obj.event, obj) + }) + event.on('go.fuck', function (data) { + console.log('fuck you from golang, ', data.name) + }) + return ins +})() + +function loadComponent(cb, url) { + var comp = Qt.createComponent(url) + + function finishCreation() { + if (comp.status === Q.Component.Ready) { + cb(comp) + } else if (comp.status === Q.Component.Error) { + // Error Handling + console.log("Error loading component:", comp.errorString()) + } + } + + if (comp.status === Q.Component.Ready) { + cb(comp) + } else { + finishCreation() + comp.statusChanged.connect(finishCreation) + } + return comp +} + +var callGoAsync +var callGoSync +;(function () { + var promiseMap = {} + callGoAsync = function (action, param, chan) { + param = param || {} + var callId = action + (+new Date) + ~~(Math.random() * 1e5) + var promise = new P.Promise(function (resolve, reject, progress) { + var handler = { + callId: callId, + resolve: resolve, + reject: reject, + progress: progress + } + promiseMap[callId] = handler + bridge.callAsync(JSON.stringify({chan: !!chan, action: action, param: param, callId: callId})) + }) + promise.callId = callId + return promise + } + event.on('go.call.ret', function (data) { + var callId = data.callId + var handler = promiseMap[callId] + if (!handler) { + console.error('can not find handler for call id: ', callId) + return + } + handler[data.type](data[data.type]) + if (data.type !== 'progress') { + delete promiseMap[callId] + } + }) + callGoSync = function (action, param) { + param = param || {} + var str = bridge.callSync(JSON.stringify({action: action, param: param})) + return JSON.parse(str).result + } +})() diff --git a/pc/gui/qml/main.qml b/pc/gui/qml/main.qml new file mode 100644 index 0000000..7c41f1f --- /dev/null +++ b/pc/gui/qml/main.qml @@ -0,0 +1,33 @@ +import QtQuick 2.1 +import QtQuick.Window 2.0 +import "js/util.js" as Util + +Window { + visible: true + width: 360 + height: 360 + + MouseArea { + anchors.fill: parent + onClicked: { + Qt.quit() + } + } + + Text { + id: text + text: "hello pan-light" + anchors.centerIn: parent + } + + Component.onCompleted: { + // 初始化js工具 + Util.callGoAsync('wait', { + "time": 3 + }).then(function (s) { + text.text = s + Util.callGoSync('time') + }, null, function (s) { + text.text = s + }) + } +} diff --git a/pc/gui/qml/qml.qrc b/pc/gui/qml/qml.qrc new file mode 100644 index 0000000..4a4da6d --- /dev/null +++ b/pc/gui/qml/qml.qrc @@ -0,0 +1,10 @@ + + + main.qml + comps/bridge.qml + js/util.js + js/promise.js + js/global.js + comps/timer.qml + + diff --git a/pc/gui/qt-rpc/rpc.go b/pc/gui/qt-rpc/rpc.go new file mode 100644 index 0000000..799d4a7 --- /dev/null +++ b/pc/gui/qt-rpc/rpc.go @@ -0,0 +1,36 @@ +package qt_rpc + +import ( + "log" + "time" +) + +type Gson map[string]interface{} + +var CallGoSync = func(gson *Gson) *Gson { + return &Gson{ + "error": "go rpc service not initialized", + } +} + +var CallGoAsync = func(gson *Gson) { + go func() { + result := &Gson{ + "type": "reject", + "callId": (*gson)["callId"], + "reject": "go rpc service not initialized", + } + NotifyQml("call.ret", result) + }() +} + +var NotifyQml = func(event string, data *Gson) { + log.Println("qml msg handler not initialized") +} + +func init() { + go func() { + time.Sleep(1 * time.Second) + NotifyQml("fuck", &Gson{"name": "trump"}) + }() +} diff --git a/pc/pan-light-pc.go b/pc/pan-light-pc.go new file mode 100644 index 0000000..295f807 --- /dev/null +++ b/pc/pan-light-pc.go @@ -0,0 +1,35 @@ +package main + +import ( + _ "github.com/peterq/pan-light/pc/gui/bridge" + _ "github.com/peterq/pan-light/pc/gui/comp" + _ "github.com/peterq/pan-light/pc/gui/qml" + "github.com/peterq/pan-light/qt/bindings/core" + "github.com/peterq/pan-light/qt/bindings/gui" + "github.com/peterq/pan-light/qt/bindings/qml" + "github.com/peterq/pan-light/qt/bindings/quick" + "os" +) + +var dev = true + +func main() { + if dev { + devSetup() + } + + // 开启高清 + core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true) + quick.QQuickWindow_SetDefaultAlphaBuffer(true) // 悬浮窗需要此设置 + + app := gui.NewQGuiApplication(len(os.Args), os.Args) + + engine := qml.NewQQmlApplicationEngine(nil) + engine.Load(core.NewQUrl3("qrc:/main.qml", 0)) + + app.Exec() + +} + +func devSetup() { +} diff --git a/qt/.gitignore b/qt/.gitignore new file mode 100644 index 0000000..7e7e5d3 --- /dev/null +++ b/qt/.gitignore @@ -0,0 +1,24 @@ +*.jsc +*.log +*.pro +*.pro.user +*.qmake.stash +*.qmlc +*.so +*.trace +*.vagrant +.DS_Store +Mfile* + +*/*-minimal.* +*/*.o +*/*_string.go +*/cgo_* +*/minimal_* +bindings/ + +/internal/binding/dump/* +/internal/cmd/moc/test/**/moc* +/internal/examples/**/deploy +/internal/examples/**/moc* +/internal/examples/**/rcc* \ No newline at end of file diff --git a/qt/LICENSE b/qt/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/qt/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/qt/README.md b/qt/README.md new file mode 100644 index 0000000..e43fb39 --- /dev/null +++ b/qt/README.md @@ -0,0 +1,81 @@ +> 本模块基于 [therecipe/qt](https://github.com/therecipe/qt) 修改, 详细文档请访问原项目 + +--- + +Introduction +------------ + +[Qt](https://en.wikipedia.org/wiki/Qt_(software)) is a cross-platform application framework that is used for developing application software that can be run on various software and hardware platforms with little or no change in the underlying codebase. + +[Go](https://en.wikipedia.org/wiki/Go_(programming_language)) (often referred to as golang) is a programming language created at Google. + +This package allows you to write Qt applications entirely in Go and makes deploying them later very easy. + +[Gallery](https://github.com/therecipe/qt/wiki/Gallery) of example applications making use of this package. + +[WebAssembly Demo](https://therecipe.github.io/widgets_playground) | *[repo](https://github.com/therecipe/widgets_playground)* + +Status +------ + +Almost all Qt functions and classes are accessible from Go and you should be able to find everything you need to build fully featured applications. + +(A special exception are the WebEngine/WebView packages, these aren't available for Windows yet.) + +Installation +------------ + +##### Windows [(more info)](https://github.com/therecipe/qt/wiki/Installation-on-Windows) + +```powershell +go get -u -v github.com/therecipe/qt/cmd/... && for /f %v in ('go env GOPATH') do %v\bin\qtsetup test && %v\bin\qtsetup +``` + +##### macOS [(more info)](https://github.com/therecipe/qt/wiki/Installation-on-macOS) + +```bash +xcode-select --install; go get -u -v github.com/therecipe/qt/cmd/... && $(go env GOPATH)/bin/qtsetup test && $(go env GOPATH)/bin/qtsetup +``` + +##### Linux [(more info)](https://github.com/therecipe/qt/wiki/Installation-on-Linux) + +```bash +go get -u -v github.com/therecipe/qt/cmd/... && $(go env GOPATH)/bin/qtsetup test && $(go env GOPATH)/bin/qtsetup +``` + +Resources +--------- + +- [General Installation](https://github.com/therecipe/qt/wiki/Installation) +- [Getting Started](https://github.com/therecipe/qt/wiki/Getting-Started) +- [Wiki](https://github.com/therecipe/qt/wiki) +- [Qt Documentation](https://doc.qt.io/qt-5/classes.html) +- [FAQ](https://github.com/therecipe/qt/wiki/FAQ) +- [#qt-binding](https://gophers.slack.com/messages/qt-binding/details) Slack channel ([invite](https://invite.slack.golangbridge.org)\) + +Deployment Targets +------------------ + +| Target | Arch | Linkage | Docker Deployment | Host OS | +|:------------------------:|:--------:|:----------------:|:-----------------:|:-------:| +| Windows | 32 / 64 | dynamic / static | Yes | Any | +| Linux | 64 | dynamic / system | Yes | Any | +| Android (+Wear) | arm | dynamic | Yes | Any | +| Android-Emulator (+Wear) | 32 | dynamic | Yes | Any | +| Raspberry Pi (1/2/3) | arm | dynamic / system | Yes | Any | +| SailfishOS | arm | system | Yes | Any | +| SailfishOS-Emulator | 32 | system | Yes | Any | +| Ubuntu Touch | arm / 64 | system | Yes | Any | +| JavaScript | 32 | static | Yes | Any | +| WebAssembly | 32 | static | Yes | Any | +| macOS | 64 | dynamic | No | macOS | +| iOS | arm64 | static | No | macOS | +| iOS-Simulator | 64 | static | No | macOS | +| AsteroidOS | arm | system | No | Linux | + +License +------- + +This package is released under [LGPLv3](https://opensource.org/licenses/LGPL-3.0) + +Qt is available under multiple [licenses](https://www.qt.io/licensing) diff --git a/qt/cmd/env/darwin_amd64.go b/qt/cmd/env/darwin_amd64.go new file mode 100644 index 0000000..eaa4c13 --- /dev/null +++ b/qt/cmd/env/darwin_amd64.go @@ -0,0 +1,5 @@ +// +build !no_env,darwin,amd64 + +package env + +import _ "github.com/therecipe/env_darwin_amd64_512" diff --git a/qt/cmd/env/linux_amd64.go b/qt/cmd/env/linux_amd64.go new file mode 100644 index 0000000..a72688c --- /dev/null +++ b/qt/cmd/env/linux_amd64.go @@ -0,0 +1,5 @@ +// +build !no_env,linux,amd64 + +package env + +import _ "github.com/therecipe/env_linux_amd64_512" diff --git a/qt/cmd/env/windows_amd64.go b/qt/cmd/env/windows_amd64.go new file mode 100644 index 0000000..c8e4f0e --- /dev/null +++ b/qt/cmd/env/windows_amd64.go @@ -0,0 +1,5 @@ +// +build !no_env,windows,amd64 + +package env + +import _ "github.com/therecipe/env_windows_amd64_512" diff --git a/qt/cmd/qtdeploy/main.go b/qt/cmd/qtdeploy/main.go new file mode 100644 index 0000000..78e1207 --- /dev/null +++ b/qt/cmd/qtdeploy/main.go @@ -0,0 +1,137 @@ +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/deploy" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func main() { + flag.Usage = func() { + println("Usage: qtdeploy [-docker] [mode] [target] [path/to/project]\n") + + println("Flags:\n") + flag.PrintDefaults() + println() + + println("Modes:\n") + for _, m := range []struct{ name, desc string }{ + {"build", "compile and bundle"}, + {"run", "run the binary"}, + {"test", "build and run"}, + {"help", "print help"}, + } { + fmt.Printf(" %v%v%v\n", m.name, strings.Repeat(" ", 12-len(m.name)), m.desc) + } + println() + + println("Targets:\n") + //TODO: + println() + + os.Exit(0) + } + + var docker bool + flag.BoolVar(&docker, "docker", false, "run command inside docker container") + + var vagrant bool + flag.BoolVar(&vagrant, "vagrant", false, "run command inside vagrant vm") + + var ldFlags string + flag.StringVar(&ldFlags, "ldflags", "", "arguments to pass on each go tool link invocation") + + var fast bool + flag.BoolVar(&fast, "fast", false, "use cached moc, minimal and dependencies (works for: windows, darwin, linux)") + + var tags string + flag.StringVar(&tags, "tags", "", "a list of build tags to consider satisfied during the build") + + var device string + flag.StringVar(&device, "device", "", "a device UUID to be used by the iOS simulator") + + var comply bool + flag.BoolVar(&comply, "comply", false, "dump object code to make it easier to comply with LGPL obligations for proprietary developments") + + if cmd.ParseFlags() { + flag.Usage() + } + + mode := "test" + target := runtime.GOOS + path, err := os.Getwd() + if err != nil { + utils.Log.WithError(err).Debug("failed to get cwd") + } + + switch flag.NArg() { + case 0: + case 1: + mode = flag.Arg(0) + case 2: + mode = flag.Arg(0) + target = flag.Arg(1) + case 3: + mode = flag.Arg(0) + target = flag.Arg(1) + path = flag.Arg(2) + default: + flag.Usage() + } + + if mode == "help" { + flag.Usage() + } + + var vagrant_system string + if target_splitted := strings.Split(target, "/"); vagrant && len(target_splitted) == 2 { + vagrant_system = target_splitted[0] + target = target_splitted[1] + } + + if target == "desktop" { + target = runtime.GOOS + } + utils.CheckBuildTarget(target) + cmd.InitEnv(target) + + if !filepath.IsAbs(path) { + oPath := path + path, err = filepath.Abs(path) + if err != nil || !utils.ExistsDir(path) { + utils.Log.WithError(err).WithField("path", path).Debug("can't resolve absolute path") + dirFunc := func() (string, error) { + out, err := utils.RunCmdOptionalError(utils.GoList("{{.Dir}}", oPath), "get pkg dir") + return strings.TrimSpace(out), err + } + if dir, err := dirFunc(); err != nil { + utils.RunCmd(exec.Command("go", "get", "-d", "-v", oPath), "go get pkg") + path, _ = dirFunc() + } else { + path = dir + } + } + } + + if !(target == runtime.GOOS || target == "js" || target == "wasm") { + fast = false + } + if (docker || vagrant) && !(target == "js" || target == "wasm") { + fast = false + } + + if target == "js" || target == "wasm" { //TODO: remove for module support + resolve dependencies + os.Setenv("GOCACHE", "off") + } + + deploy.Deploy(mode, target, path, docker, ldFlags, tags, fast, device, vagrant, vagrant_system, comply) +} diff --git a/qt/cmd/qtminimal/main.go b/qt/cmd/qtminimal/main.go new file mode 100644 index 0000000..d2c09a0 --- /dev/null +++ b/qt/cmd/qtminimal/main.go @@ -0,0 +1,104 @@ +package main + +import ( + "flag" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/minimal" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func main() { + flag.Usage = func() { + println("Usage: qtminimal [-docker] [target] [path/to/project]\n") + + println("Flags:\n") + flag.PrintDefaults() + println() + + println("Targets:\n") + //TODO: + println() + + os.Exit(0) + } + + var docker bool + flag.BoolVar(&docker, "docker", false, "run command inside docker container") + + var vagrant bool + flag.BoolVar(&vagrant, "vagrant", false, "run command inside vagrant vm") + + var tags string + flag.StringVar(&tags, "tags", "", "a list of build tags to consider satisfied during the build") + + if cmd.ParseFlags() { + flag.Usage() + } + + target := runtime.GOOS + path, err := os.Getwd() + if err != nil { + utils.Log.WithError(err).Debug("failed to get cwd") + } + + switch flag.NArg() { + case 0: + case 1: + target = flag.Arg(0) + case 2: + target = flag.Arg(0) + path = flag.Arg(1) + default: + flag.Usage() + } + + var vagrant_system string + if target_splitted := strings.Split(target, "/"); vagrant && len(target_splitted) == 2 { + vagrant_system = target_splitted[0] + target = target_splitted[1] + } + + if target == "desktop" { + target = runtime.GOOS + } + utils.CheckBuildTarget(target) + cmd.InitEnv(target) + + if !filepath.IsAbs(path) { + oPath := path + path, err = filepath.Abs(path) + if err != nil || !utils.ExistsDir(path) { + utils.Log.WithError(err).WithField("path", path).Debug("can't resolve absolute path") + dirFunc := func() (string, error) { + out, err := utils.RunCmdOptionalError(utils.GoList("{{.Dir}}", oPath), "get pkg dir") + return strings.TrimSpace(out), err + } + if dir, err := dirFunc(); err != nil { + utils.RunCmd(exec.Command("go", "get", "-d", "-v", oPath), "go get pkg") + path, _ = dirFunc() + } else { + path = dir + } + } + } + + if target == "js" || target == "wasm" { //TODO: remove for module support + resolve dependencies + os.Setenv("GOCACHE", "off") + } + + switch { + case docker: + cmd.Docker([]string{"qtminimal", "-debug", "-tags=" + tags}, target, path, false) + case vagrant: + cmd.Vagrant([]string{"qtminimal", "-debug", "-tags=" + tags}, target, path, false, vagrant_system) + default: + minimal.Minimal(path, target, tags) + } +} diff --git a/qt/cmd/qtmoc/main.go b/qt/cmd/qtmoc/main.go new file mode 100644 index 0000000..bc09825 --- /dev/null +++ b/qt/cmd/qtmoc/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "flag" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/moc" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func main() { + flag.Usage = func() { + println("Usage: qtmoc [-docker] [target] [path/to/project]\n") + + println("Flags:\n") + flag.PrintDefaults() + println() + + println("Targets:\n") + //TODO: + println() + + os.Exit(0) + } + + var docker bool + flag.BoolVar(&docker, "docker", false, "run command inside docker container") + + var vagrant bool + flag.BoolVar(&vagrant, "vagrant", false, "run command inside vagrant vm") + + var fast bool + flag.BoolVar(&fast, "fast", false, "don't run qtmoc for dependencies") + + var tags string + flag.StringVar(&tags, "tags", "", "a list of build tags to consider satisfied during the build") + + var slow bool + flag.BoolVar(&slow, "slow", false, "reduce qtmoc's resource usage") + + if cmd.ParseFlags() { + flag.Usage() + } + + target := runtime.GOOS + path, err := os.Getwd() + if err != nil { + utils.Log.WithError(err).Debug("failed to get cwd") + } + + switch flag.NArg() { + case 0: + case 1: + target = flag.Arg(0) + case 2: + target = flag.Arg(0) + path = flag.Arg(1) + default: + flag.Usage() + } + + var vagrant_system string + if target_splitted := strings.Split(target, "/"); vagrant && len(target_splitted) == 2 { + vagrant_system = target_splitted[0] + target = target_splitted[1] + } + + if target == "desktop" { + target = runtime.GOOS + } + utils.CheckBuildTarget(target) + cmd.InitEnv(target) + + if !filepath.IsAbs(path) { + oPath := path + path, err = filepath.Abs(path) + if err != nil || !utils.ExistsDir(path) { + utils.Log.WithError(err).WithField("path", path).Debug("can't resolve absolute path") + dirFunc := func() (string, error) { + out, err := utils.RunCmdOptionalError(utils.GoList("{{.Dir}}", oPath), "get pkg dir") + return strings.TrimSpace(out), err + } + if dir, err := dirFunc(); err != nil { + utils.RunCmd(exec.Command("go", "get", "-d", "-v", oPath), "go get pkg") + path, _ = dirFunc() + } else { + path = dir + } + } + } + + if target == "js" || target == "wasm" { //TODO: remove for module support + resolve dependencies + os.Setenv("GOCACHE", "off") + } + + switch { + case docker: + cmd.Docker([]string{"qtmoc", "-debug", "-tags=" + tags}, target, path, false) + case vagrant: + cmd.Vagrant([]string{"qtmoc", "-debug", "-tags=" + tags}, target, path, false, vagrant_system) + default: + moc.Moc(path, target, tags, fast, slow) + } +} diff --git a/qt/cmd/qtrcc/main.go b/qt/cmd/qtrcc/main.go new file mode 100644 index 0000000..93e023d --- /dev/null +++ b/qt/cmd/qtrcc/main.go @@ -0,0 +1,113 @@ +package main + +import ( + "flag" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/rcc" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func main() { + flag.Usage = func() { + println("Usage: qtrcc [-docker] [target] [path/to/project]\n") + + println("Flags:\n") + flag.PrintDefaults() + println() + + println("Targets:\n") + //TODO: + println() + + os.Exit(0) + } + + var docker bool + flag.BoolVar(&docker, "docker", false, "run command inside docker container") + + var vagrant bool + flag.BoolVar(&vagrant, "vagrant", false, "run command inside vagrant vm") + + var output string + flag.StringVar(&output, "o", os.Getenv("QTRCC_OUTPUT_DIR"), "specify an alternative output dir") + + var tags string + flag.StringVar(&tags, "tags", "", "a list of build tags to consider satisfied during the build") + + if cmd.ParseFlags() { + flag.Usage() + } + + target := runtime.GOOS + path, err := os.Getwd() + if err != nil { + utils.Log.WithError(err).Debug("failed to get cwd") + } + + switch flag.NArg() { + case 0: + case 1: + target = flag.Arg(0) + case 2: + target = flag.Arg(0) + path = flag.Arg(1) + default: + flag.Usage() + } + + var vagrant_system string + if target_splitted := strings.Split(target, "/"); vagrant && len(target_splitted) == 2 { + vagrant_system = target_splitted[0] + target = target_splitted[1] + } + + if target == "desktop" { + target = runtime.GOOS + } + utils.CheckBuildTarget(target) + cmd.InitEnv(target) + + if !filepath.IsAbs(path) { + oPath := path + path, err = filepath.Abs(path) + if err != nil || !utils.ExistsDir(path) { + utils.Log.WithError(err).WithField("path", path).Debug("can't resolve absolute path") + dirFunc := func() (string, error) { + out, err := utils.RunCmdOptionalError(utils.GoList("{{.Dir}}", oPath), "get pkg dir") + return strings.TrimSpace(out), err + } + if dir, err := dirFunc(); err != nil { + utils.RunCmd(exec.Command("go", "get", "-d", "-v", oPath), "go get pkg") + path, _ = dirFunc() + } else { + path = dir + } + } + } + if output != "" && !filepath.IsAbs(output) { + output, err = filepath.Abs(output) + if err != nil { + utils.Log.WithError(err).WithField("output", output).Fatal("can't resolve absolute path") + } + } + + if target == "js" || target == "wasm" { //TODO: remove for module support + resolve dependencies + os.Setenv("GOCACHE", "off") + } + + switch { + case docker: + cmd.Docker([]string{"qtrcc", "-debug"}, target, path, false) + case vagrant: + cmd.Vagrant([]string{"qtrcc", "-debug"}, target, path, false, vagrant_system) + default: + rcc.Rcc(path, target, tags, output) + } +} diff --git a/qt/cmd/qtsetup/main.go b/qt/cmd/qtsetup/main.go new file mode 100644 index 0000000..cdae8d3 --- /dev/null +++ b/qt/cmd/qtsetup/main.go @@ -0,0 +1,120 @@ +package main + +import ( + "flag" + "fmt" + "os" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/setup" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func main() { + flag.Usage = func() { + println("Usage: qtsetup [-debug] [mode] [target]\n") + + println("Flags:\n") + flag.PrintDefaults() + println() + + println("Modes:\n") + for _, m := range []struct{ name, desc string }{ + {"prep", "symlink tooling into the PATH"}, + {"check", "perform some basic env checks"}, + {"generate", "generate code for all packages"}, + {"install", "go install all packages"}, + {"test", "build and test some examples"}, + {"full", "run all of the above"}, + {"help", "print help"}, + {"update", "update 'cmd' and 'tool-chain/cmd'"}, + {"upgrade", "update everything"}, + } { + fmt.Printf(" %v%v%v\n", m.name, strings.Repeat(" ", 12-len(m.name)), m.desc) + } + println() + + println("Targets:\n") + //TODO: + println() + + os.Exit(0) + } + + var docker bool + flag.BoolVar(&docker, "docker", false, "run command inside docker container") + + var vagrant bool + flag.BoolVar(&vagrant, "vagrant", false, "run command inside vagrant vm") + + var dynamic bool + if runtime.GOOS != "windows" { + flag.BoolVar(&dynamic, "dynamic", false, "create and use semi-dynamic libraries during the generation and installation process (experimental; no real replacement for dynamic linking)") + } + + if cmd.ParseFlags() { + flag.Usage() + } + + mode := "full" + target := runtime.GOOS + + switch flag.NArg() { + case 0: + case 1: + mode = flag.Arg(0) + case 2: + mode = flag.Arg(0) + target = flag.Arg(1) + default: + flag.Usage() + } + + var vagrant_system string + if target_splitted := strings.Split(target, "/"); vagrant && len(target_splitted) == 2 { + vagrant_system = target_splitted[0] + target = target_splitted[1] + } + + if target == "desktop" { + target = runtime.GOOS + } + utils.CheckBuildTarget(target) + cmd.InitEnv(target) + + if dynamic && (target == runtime.GOOS || target == "js" || target == "wasm") { + os.Setenv("QT_DYNAMIC_SETUP", "true") + } + + if target == "js" || target == "wasm" { //TODO: remove for module support + resolve dependencies + os.Setenv("GOCACHE", "off") + } + + switch mode { + case "prep": + setup.Prep() + case "check": + setup.Check(target, docker, vagrant) + case "generate": + setup.Generate(target, docker, vagrant) + case "install": + setup.Install(target, docker, vagrant) + case "test": + setup.Test(target, docker, vagrant, vagrant_system) + case "full": + setup.Prep() + setup.Check(target, docker, vagrant) + setup.Generate(target, docker, vagrant) + setup.Install(target, docker, vagrant) + setup.Test(target, docker, vagrant, vagrant_system) + case "update": + setup.Update() + case "upgrade": + setup.Upgrade() + default: + flag.Usage() + } +} diff --git a/qt/go.mod b/qt/go.mod new file mode 100644 index 0000000..12a9149 --- /dev/null +++ b/qt/go.mod @@ -0,0 +1,22 @@ +module github.com/peterq/pan-light/qt + +require ( + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/kisielk/gotool v1.0.0 // indirect + github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 // indirect + github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab // indirect + github.com/shurcooL/go v0.0.0-20181215222900-0143a8f55f04 // indirect + github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 // indirect + github.com/sirupsen/logrus v1.4.0 + github.com/spf13/cobra v0.0.3 // indirect + github.com/spf13/pflag v1.0.3 // indirect + github.com/stretchr/testify v1.2.2 + github.com/therecipe/env_darwin_amd64_512 v0.0.0-20190102210040-529084f510b3 + github.com/therecipe/env_linux_amd64_512 v0.0.0-20190102180622-9b3faedb5806 + github.com/therecipe/env_windows_amd64_512 v0.0.0-20190121195954-78ba6026af9b + github.com/therecipe/env_windows_amd64_512/Tools v0.0.0-20190121195954-78ba6026af9b // indirect + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 + golang.org/x/tools v0.0.0-20190319232107-3f1ed9edd1b4 +) diff --git a/qt/go.sum b/qt/go.sum new file mode 100644 index 0000000..c467abd --- /dev/null +++ b/qt/go.sum @@ -0,0 +1,50 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shurcooL/go v0.0.0-20181215222900-0143a8f55f04/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/therecipe/env_darwin_amd64_512 v0.0.0-20190102210040-529084f510b3 h1:PA8Ye1gIvi075ue+vrnrxO+FwzLlqPdtXAYaOoFawgQ= +github.com/therecipe/env_darwin_amd64_512 v0.0.0-20190102210040-529084f510b3/go.mod h1:4O+Hy+lamRLGBSpC6us4ri+GigPeYdeG9wQRZFH3Rkc= +github.com/therecipe/env_linux_amd64_512 v0.0.0-20190102180622-9b3faedb5806 h1:jDlD5+hUuk3Jqv/p3pXWg6XnSHg+vP2C+TawC4PAwZY= +github.com/therecipe/env_linux_amd64_512 v0.0.0-20190102180622-9b3faedb5806/go.mod h1:rUlYPwVtCiv2SKco9iedVR3TouC3WvEzagEWtL2Mim8= +github.com/therecipe/env_windows_amd64_512 v0.0.0-20190121195954-78ba6026af9b h1:6ZI+KleTgN9qERNFwcraUQglw7jWf8fGwaUgYWH+C6k= +github.com/therecipe/env_windows_amd64_512 v0.0.0-20190121195954-78ba6026af9b/go.mod h1:hcI7eNXyJoOLCMhct/6aNYySZ4s8pXEejlkSF0tdlww= +github.com/therecipe/env_windows_amd64_512/Tools v0.0.0-20190121195954-78ba6026af9b h1:gqAAI91SAWaZGnrtYbfrJ688I5WKiiL1hILRF5WtXZ8= +github.com/therecipe/env_windows_amd64_512/Tools v0.0.0-20190121195954-78ba6026af9b/go.mod h1:uo9CZHpTlY/tCnFZXyhV/weqZGYWsbYmPOU8UANqU2E= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181218192612-074acd46bca6 h1:MXtOG7w2ND9qNCUZSDBGll/SpVIq7ftozR9I8/JGBHY= +golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e h1:XEcLGV2fKy3FrsoJVCkX+lMhqc9Suj7J5L/wldA1wu4= +golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190319232107-3f1ed9edd1b4 h1:4oAPsdy/MJIeaCzEMEhYwYBU/gHkXH52Xa4M+0GBHfA= +golang.org/x/tools v0.0.0-20190319232107-3f1ed9edd1b4/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/qt/qt.go b/qt/qt.go new file mode 100644 index 0000000..e3f6220 --- /dev/null +++ b/qt/qt.go @@ -0,0 +1,190 @@ +package qt + +import ( + "log" + "os" + "runtime" + "strings" + "sync" + "unsafe" +) + +var ( + Logger = log.New(os.Stderr, "", log.Ltime) + + signals = make(map[unsafe.Pointer]map[string]interface{}) + signalsJNI = make(map[string]map[string]interface{}) + signalsMutex = new(sync.Mutex) + + objects = make(map[unsafe.Pointer]interface{}) + objectsMutex = new(sync.Mutex) + + objectsTemp = make(map[unsafe.Pointer]interface{}) + objectsTempMutex = new(sync.Mutex) +) + +func init() { runtime.LockOSThread() } + +func ExistsSignal(cPtr unsafe.Pointer, signal string) (exists bool) { + signalsMutex.Lock() + _, exists = signals[cPtr][signal] + signalsMutex.Unlock() + return +} + +func LendSignal(cPtr unsafe.Pointer, signal string) (s interface{}) { + signalsMutex.Lock() + s = signals[cPtr][signal] + signalsMutex.Unlock() + return +} + +func lendSignalJNI(cPtr, signal string) (s interface{}) { + signalsMutex.Lock() + s = signalsJNI[cPtr][signal] + signalsMutex.Unlock() + return +} + +func GetSignal(cPtr interface{}, signal string) interface{} { + if dcPtr, ok := cPtr.(unsafe.Pointer); ok { + if signal == "destroyed" || strings.HasPrefix(signal, "~") { + defer DisconnectAllSignals(dcPtr, signal) + } + return LendSignal(dcPtr, signal) + } + return lendSignalJNI(cPtr.(string), signal) +} + +func ConnectSignal(cPtr interface{}, signal string, function interface{}) { + if dcPtr, ok := cPtr.(unsafe.Pointer); ok { + signalsMutex.Lock() + if s, exists := signals[dcPtr]; !exists { + signals[dcPtr] = map[string]interface{}{signal: function} + } else { + s[signal] = function + } + signalsMutex.Unlock() + } else { + connectSignalJNI(cPtr.(string), signal, function) + } +} + +func connectSignalJNI(cPtr, signal string, function interface{}) { + signalsMutex.Lock() + if s, exists := signalsJNI[cPtr]; !exists { + signalsJNI[cPtr] = map[string]interface{}{signal: function} + } else { + s[signal] = function + } + signalsMutex.Unlock() +} + +func DisconnectSignal(cPtr interface{}, signal string) { + if dcPtr, ok := cPtr.(unsafe.Pointer); ok { + signalsMutex.Lock() + delete(signals[dcPtr], signal) + signalsMutex.Unlock() + } else { + disconnectSignalJNI(cPtr.(string), signal) + } +} + +func disconnectSignalJNI(cPtr, signal string) { + signalsMutex.Lock() + delete(signalsJNI[cPtr], signal) + signalsMutex.Unlock() +} + +func DisconnectAllSignals(cPtr unsafe.Pointer, signal string) { + signalsMutex.Lock() + if s, exists := signals[cPtr]["destroyed"]; signal != "destroyed" && exists { + signals[cPtr] = map[string]interface{}{"destroyed": s} + } else { + delete(signals, cPtr) + } + signalsMutex.Unlock() + if signal == "destroyed" { + Unregister(cPtr) + } +} + +func DumpSignals() { + Debug("##############################\tSIGNALSTABLE_START\t##############################") + signalsMutex.Lock() + for cPtr, entry := range signals { + Debug(cPtr, entry) + } + signalsMutex.Unlock() + Debug("##############################\tSIGNALSTABLE_END\t##############################") +} + +func CountSignals() (c int) { + signalsMutex.Lock() + c = len(signals) + signalsMutex.Unlock() + return +} + +func GoBoolToInt(b bool) int8 { + if b { + return 1 + } + return 0 +} + +func Recover(fn string) { + if recover() != nil { + Debug("RECOVERED:", fn) + } +} + +func Debug(fn ...interface{}) { + if strings.ToLower(os.Getenv("QT_DEBUG")) == "true" || runtime.GOARCH == "js" || runtime.GOARCH == "wasm" { + Logger.Println(fn...) + } +} + +func ClearSignals() { + signalsMutex.Lock() + signals = make(map[unsafe.Pointer]map[string]interface{}) + signalsMutex.Unlock() +} + +func Register(cPtr unsafe.Pointer, gPtr interface{}) { + objectsMutex.Lock() + objects[cPtr] = gPtr + objectsMutex.Unlock() +} + +func Receive(cPtr unsafe.Pointer) (o interface{}, ok bool) { + objectsMutex.Lock() + o, ok = objects[cPtr] + objectsMutex.Unlock() + return +} + +func Unregister(cPtr unsafe.Pointer) { + objectsMutex.Lock() + delete(objects, cPtr) + objectsMutex.Unlock() +} + +func RegisterTemp(cPtr unsafe.Pointer, gPtr interface{}) { + objectsTempMutex.Lock() + objectsTemp[cPtr] = gPtr + objectsTempMutex.Unlock() +} + +func ReceiveTemp(cPtr unsafe.Pointer) (o interface{}, ok bool) { + objectsTempMutex.Lock() + o, ok = objectsTemp[cPtr] + objectsTempMutex.Unlock() + return +} + +func UnregisterTemp(cPtr unsafe.Pointer) { + objectsTempMutex.Lock() + delete(objectsTemp, cPtr) + objectsTempMutex.Unlock() +} diff --git a/qt/qt_android.go b/qt/qt_android.go new file mode 100644 index 0000000..1f9b487 --- /dev/null +++ b/qt/qt_android.go @@ -0,0 +1,92 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qt + +/* +To view the log output run: +adb logcat GoLog:I *:S +*/ + +// Android redirects stdout and stderr to /dev/null. +// As these are common debugging utilities in Go, +// we redirect them to logcat. +// +// Unfortunately, logcat is line oriented, so we must buffer. + +/* +#cgo LDFLAGS: -landroid -llog +#include +#include +#include +*/ +import "C" + +import ( + "bufio" + "log" + "os" + "syscall" + "unsafe" +) + +var ( + ctag = C.CString("GoLog") + // Store the writer end of the redirected stderr and stdout + // so that they are not garbage collected and closed. + stderr, stdout *os.File +) + +type infoWriter struct{} + +func (infoWriter) Write(p []byte) (n int, err error) { + cstr := C.CString(string(p)) + C.__android_log_write(C.ANDROID_LOG_INFO, ctag, cstr) + C.free(unsafe.Pointer(cstr)) + return len(p), nil +} + +func lineLog(f *os.File, priority C.int) { + const logSize = 1024 // matches android/log.h. + r := bufio.NewReaderSize(f, logSize) + for { + line, _, err := r.ReadLine() + str := string(line) + if err != nil { + str += " " + err.Error() + } + cstr := C.CString(str) + C.__android_log_write(priority, ctag, cstr) + C.free(unsafe.Pointer(cstr)) + if err != nil { + break + } + } +} + +func init() { + log.SetOutput(infoWriter{}) + // android logcat includes all of log.LstdFlags + log.SetFlags(log.Flags() &^ log.LstdFlags) + + r, w, err := os.Pipe() + if err != nil { + panic(err) + } + stderr = w + if err := syscall.Dup3(int(w.Fd()), int(os.Stderr.Fd()), 0); err != nil { + panic(err) + } + go lineLog(r, C.ANDROID_LOG_ERROR) + + r, w, err = os.Pipe() + if err != nil { + panic(err) + } + stdout = w + if err := syscall.Dup3(int(w.Fd()), int(os.Stdout.Fd()), 0); err != nil { + panic(err) + } + go lineLog(r, C.ANDROID_LOG_INFO) +} diff --git a/qt/qt_js.go b/qt/qt_js.go new file mode 100644 index 0000000..f1ce59d --- /dev/null +++ b/qt/qt_js.go @@ -0,0 +1,29 @@ +// +build js,!wasm + +package qt + +import "github.com/gopherjs/gopherjs/js" + +var Global = js.Global +var Module = Global.Call("eval", "Module") + +func MakeWrapper(i interface{}) *js.Object { + o := js.InternalObject(i) + methods := o.Get("constructor").Get("methods") + for i := 0; i < methods.Length(); i++ { + m := methods.Index(i) + if m.Get("pkg").String() != "" { // not exported + continue + } + if o.Get(m.Get("name").String()) == js.Undefined { + o.Set(m.Get("name").String(), func(args ...*js.Object) *js.Object { + return js.Global.Call("$externalizeFunction", o.Get(m.Get("prop").String()), m.Get("typ"), true).Call("apply", o, args) + }) + } + } + return o +} + +// + +var WASM = Module //TODO: remove diff --git a/qt/qt_notjs.go b/qt/qt_notjs.go new file mode 100644 index 0000000..524f2ce --- /dev/null +++ b/qt/qt_notjs.go @@ -0,0 +1,11 @@ +// +build !js + +package qt + +type jsValue interface { + Call(...string) jsValue + Int() int +} + +var Global jsValue +var Module jsValue diff --git a/qt/qt_wasm.go b/qt/qt_wasm.go new file mode 100644 index 0000000..b684fa7 --- /dev/null +++ b/qt/qt_wasm.go @@ -0,0 +1,24 @@ +// +build js,wasm + +package qt + +import ( + "syscall/js" + "unsafe" +) + +func init() { + WASM.Set("_callbackReleaseTypedArray", js.NewCallback(func(_ js.Value, args []js.Value) interface{} { + (*js.TypedArray)(unsafe.Pointer(uintptr(args[0].Int()))).Release() + return nil + })) +} + +var Global = js.Global() +var Module = Global.Call("eval", "Module") + +//TODO: func MakeWrapper(i interface{}) *js.Value + +// + +var WASM = Module //TODO: remove diff --git a/qt/tool-chain/binding/converter/body_input_cpp.go b/qt/tool-chain/binding/converter/body_input_cpp.go new file mode 100644 index 0000000..a30d682 --- /dev/null +++ b/qt/tool-chain/binding/converter/body_input_cpp.go @@ -0,0 +1,221 @@ +package converter + +import ( + "crypto/sha1" + "encoding/hex" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func CppInputParameters(function *parser.Function) string { + + var input = make([]string, len(function.Parameters)) + + for i, parameter := range function.Parameters { + input[i] = CppInput(parameter.Name, parameter.Value, function) + } + + if function.Fullname == "QWebEnginePage::print" { + input = append(input, "[](bool){}") + } + + return strings.Join(input, ", ") +} + +func CppInputParametersForSlotInvoke(function *parser.Function) string { + if len(function.Parameters) == 0 { + return "" + } + + input := make([]string, len(function.Parameters)) + + for i, parameter := range function.Parameters { + input[i] = fmt.Sprintf("Q_ARG(%v, %v)", CppInputParametersForSlotArguments(function, parameter), CppInput(parameter.Name, parameter.Value, function)) + + if c, _ := function.Class(); c.Module == parser.MOC && parser.IsPackedMap(parameter.Value) && function.IsMocFunction { + var tHash = sha1.New() + tHash.Write([]byte(parameter.Value)) + input[i] = strings.Replace(input[i], parser.CleanValue(parameter.Value), fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])), -1) + } + } + + return fmt.Sprintf(", %v", strings.Join(input, ", ")) +} + +func CppInputParametersForSlotArguments(function *parser.Function, parameter *parser.Parameter) string { + var con string + if strings.Contains(parameter.Value, "const ") { + con = "const " + } + + switch { + case strings.Contains(parameter.Value, "*"): + if parser.IsPackedList(parameter.Value) || parser.IsPackedMap(parameter.Value) { + return fmt.Sprintf("%v%v", con, parser.CleanValue(parameter.Value)) + } + return fmt.Sprintf("%v%v*", con, parser.CleanValue(parameter.Value)) + + case isEnum(function.ClassName(), parameter.Value): + if function.Meta == parser.SLOT && function.SignalMode == "" && parser.CleanValue(parameter.Value) == "Qt::Alignment" { + return parser.CleanValue(parameter.Value) + } + return fmt.Sprintf("%v%v", con, cppEnum(function, parameter.Value, true)) + + default: + return fmt.Sprintf("%v%v", con, parser.CleanValue(parameter.Value)) + } +} + +func CppInputParametersForSignalConnect(function *parser.Function) string { + + var input = make([]string, len(function.Parameters)) + + for i, parameter := range function.Parameters { + if isEnum(function.ClassName(), parameter.Value) { + input[i] = cppEnum(function, parameter.Value, true) + } else { + input[i] = parameter.Value + } + } + + return strings.Join(input, ", ") +} + +func CppInputParametersForCallbackHeader(function *parser.Function) string { + + var input = make([]string, len(function.Parameters)) + + for i, parameter := range function.Parameters { + if isEnum(function.ClassName(), parameter.Value) { + input[i] = fmt.Sprintf("%v %v", cppEnum(function, parameter.Value, true), parser.CleanName(parameter.Name, parameter.Value)) + } else { + var c, _ = function.Class() + if parser.IsPackedMap(parameter.Value) && c.Module == parser.MOC && function.IsMocFunction { + var tHash = sha1.New() + tHash.Write([]byte(parameter.Value)) + input[i] = fmt.Sprintf("%v %v", fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])), parser.CleanName(parameter.Name, parameter.Value)) + } else { + if parser.IsPackedList(parameter.Value) || parser.IsPackedMap(parameter.Value) { + input[i] = fmt.Sprintf("%v %v", function.OgParameters[i].Value, parser.CleanName(parameter.Name, parameter.Value)) + } else { + input[i] = fmt.Sprintf("%v %v", parameter.Value, parser.CleanName(parameter.Name, parameter.Value)) + } + } + } + } + + return strings.Join(input, ", ") +} + +func CppInputParametersForCallbackBody(function *parser.Function) string { + var input = make([]string, len(function.Parameters)+1) + + if parser.UseJs() { + if strings.Contains(strings.Split(function.Signature, ")")[1], "const") { + input[0] = fmt.Sprintf("reinterpret_cast(const_cast(static_cast(this)))") + } else { + input[0] = "reinterpret_cast(this)" + } + } else { + if strings.Contains(strings.Split(function.Signature, ")")[1], "const") { + input[0] = fmt.Sprintf("const_cast(static_cast(this))") + } else { + input[0] = "this" + } + } + + for i, parameter := range function.Parameters { + input[i+1] = cppOutputPacked(parameter.Name, parameter.Value, function) + } + + return strings.Join(input, ", ") +} + +func CppInputParametersForCallbackBodyPrePack(function *parser.Function) string { + var input = make([]string, 0) + + for _, parameter := range function.Parameters { + if packed := cppOutputPack(parameter.Name, parameter.Value, function); packed != "" { + input = append(input, packed) + } + } + + return strings.Join(input, "") +} + +func CppRegisterMetaType(function *parser.Function) string { + var out = make([]string, 0) + + for _, p := range function.Parameters { + if isEnum(function.ClassName(), p.Value) { + out = append(out, cppEnum(function, p.Value, true)) + } + } + + if isEnum(function.ClassName(), function.Output) { + out = append(out, cppEnum(function, function.Output, true)) + } + + //TODO: these should ideally be registered with Q_DECLARE_METATYPE instead; also relocate use into registerTypes function + for i := len(out) - 1; i >= 0; i-- { + switch out[i] { + case "QAbstractAnimation::DeletionPolicy", "QTimeLine::State", "QClipboard::Mode", + "QImageReader::ImageReaderError", "QLocalSocket::LocalSocketError", "QSslSocket::SslMode", "QNearFieldShareManager::ShareModes", + "QAccelerometer::AccelerationMode", "QSensor::AxesOrientationMode", "QGeoAreaMonitorSource::Error", "QGeoPositionInfoSource::Error", + "QGeoSatelliteInfoSource::Error", "QSystemTrayIcon::MessageIcon", "QLocalSocket::LocalSocketState", "QGraphicsScene::SceneLayers", + "QSystemTrayIcon::ActivationReason", "QGraphicsBlurEffect::BlurHints", "QAbstractItemDelegate::EndEditHint", "QDockWidget::DockWidgetFeatures", + "QAbstract3DSeries::Mesh", "QAbstract3DInputHandler::InputView", "QAbstract3DGraph::ShadowQuality", + "QAbstract3DGraph::SelectionFlags", "QWebSocketProtocol::CloseCode", "QSqlDriver::NotificationSource", "QCamera::LockTypes", + "QGeoRouteReply::Error", "QWebEnginePage::Feature", "QWebEnginePage::RenderProcessTerminationStatus", "QSerialPort::Directions", + "QPrinter::Orientation", "QPrintPreviewWidget::ViewMode", "QPrintPreviewWidget::ZoomMode", "QInAppProduct::ProductType", + "Q3DCamera::CameraPreset", "Q3DTheme::ColorStyle", "Q3DTheme::Theme", "QAbstract3DAxis::AxisOrientation", "QAbstract3DGraph::OptimizationHints", + "QAbstract3DGraph::ElementType", "QImage::Format", "QItemModelBarDataProxy::MultiMatchBehavior", "QSurface3DSeries::DrawFlags", + "QAbstractBarSeries::LabelsPosition", "QScatterSeries::MarkerShape", "QWebPage::MessageSource", "QWebPage::MessageLevel", + "QWebPage::Feature", "QItemModelSurfaceDataProxy::MultiMatchBehavior", "QCategoryAxis::AxisLabelsPosition", + "QLegend::MarkerShape", "QDesignerFormWindowInterface::Feature", "QValidator::State", "QBluetoothDeviceInfo::Fields", "QValueAxis::TickType": + out = append(out[:i], out[i+1:]...) + + default: + if utils.QT_VERSION_NUM() <= 5063 { + switch out[i] { + case "QNetworkAccessManager::NetworkAccessibility": + out = append(out[:i], out[i+1:]...) + } + } else if utils.QT_VERSION_NUM() <= 5042 { + switch out[i] { + case "QAbstractAnimation::Direction", "QAbstractAnimation::State", "QAbstractItemModel::LayoutChangeHint", "QItemSelectionModel::SelectionFlags", + "QInputMethod::Action", "QMovie::MovieState", "QOpenGLDebugLogger::LoggingMode", "QWindow::Visibility", "QDnsLookup::Type", "QNetworkAccessManager::NetworkAccessibility", + "QAbstractItemView::ScrollHint", "QScroller::State", "QQmlComponent::CompilationMode", "QQuickWidget::Status", "QQuickWindow::SceneGraphError", "QQuickView::Status", + "Qt::Orientation", "Qt::DropAction", "Qt::ApplicationState", "Qt::LayoutDirection", "Qt::InputMethodQueries", "Qt::ScreenOrientation", "Qt::WindowModality", "Qt::WindowState", + "Qt::DockWidgetAreas", "Qt::DockWidgetArea", "Qt::FocusReason", "Qt::SortOrder", "Qt::ToolButtonStyle", "Qt::WindowStates", "Qt::Alignment", "Qt::ToolBarAreas": + out = append(out[:i], out[i+1:]...) + } + } + } + } + + for i := range out { + out[i] = fmt.Sprintf("\tqRegisterMetaType<%v>();", out[i]) + } + + return strings.Join(out, "\n") +} + +func CppRegisterMetaTypeProp(p *parser.Variable) string { + + if isClass(parser.CleanValue(p.Output)) { + pc, _ := p.Class() + if pc.Module != parser.State.ClassMap[parser.CleanValue(p.Output)].Module { + return p.Output + } + } + + if isEnum(p.ClassName(), parser.CleanValue(p.Output)) { + return cppEnum(&parser.Function{Fullname: p.Fullname}, parser.CleanValue(p.Output), true) + } + + return "" +} diff --git a/qt/tool-chain/binding/converter/body_input_go.go b/qt/tool-chain/binding/converter/body_input_go.go new file mode 100644 index 0000000..4d69724 --- /dev/null +++ b/qt/tool-chain/binding/converter/body_input_go.go @@ -0,0 +1,184 @@ +package converter + +import ( + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func GoInputParametersForC(function *parser.Function) string { + + var input = make([]string, 0) + + if !(function.Static || function.Meta == parser.CONSTRUCTOR) { + input = append(input, "ptr.Pointer()") + } + + if function.SignalMode == "" { + for _, parameter := range function.Parameters { + if parameter.PureGoType != "" && !parser.IsBlackListedPureGoType(parameter.PureGoType) { + input = append(input, GoInput(fmt.Sprintf("uintptr(unsafe.Pointer(%v%v))", + func() string { + if !strings.HasPrefix(parameter.PureGoType, "*") { + return "&" + } + return "" + }(), parser.CleanName(parameter.Name, parameter.Value)), parameter.Value, function, parameter.PureGoType)) + } else { + var alloc = GoInput(parameter.Name, parameter.Value, function, parameter.PureGoType) + if strings.Contains(alloc, "C.CString") || strings.Contains(alloc, "qt.GoBoolToInt(*") || strings.Contains(alloc, "*C.char") { + if parser.CleanValue(parameter.Value) == "QString" || parser.CleanValue(parameter.Value) == "QStringList" { + input = append(input, fmt.Sprintf("C.struct_%v_PackedString{data: %vC, len: %v}", strings.Title(parser.State.ClassMap[function.ClassName()].Module), parser.CleanName(parameter.Name, parameter.Value), + func() string { + if parser.IsBlackListedPureGoType(parameter.PureGoType) { + return "C.longlong(-1)" + } + if parser.CleanValue(parameter.Value) == "QStringList" { + return fmt.Sprintf("C.longlong(len(strings.Join(%v, \"|\")))", parser.CleanName(parameter.Name, parameter.Value)) + } + return fmt.Sprintf("C.longlong(len(%v))", parser.CleanName(parameter.Name, parameter.Value)) + }())) + } else { + if strings.Contains(alloc, "qt.GoBoolToInt(*") { + input = append(input, fmt.Sprintf("&%vC", parser.CleanName(parameter.Name, parameter.Value))) + } else { + input = append(input, fmt.Sprintf("%vC", parser.CleanName(parameter.Name, parameter.Value))) + } + } + } else { + input = append(input, alloc) + } + } + } + } + + return strings.Join(input, ", ") +} + +func GoInputParametersForJS(function *parser.Function) string { + + input := make([]string, 0) + + if !(function.Static || function.Meta == parser.CONSTRUCTOR) { + input = append(input, "uintptr(ptr.Pointer())") + } + + if function.SignalMode == "" { + for _, parameter := range function.Parameters { + if parameter.PureGoType != "" && !parser.IsBlackListedPureGoType(parameter.PureGoType) { + input = append(input, GoInputJS(fmt.Sprintf("%vTID", parser.CleanName(parameter.Name, parameter.Value)), parameter.Value, function, parameter.PureGoType)) + } else { + alloc := GoInputJS(parameter.Name, parameter.Value, function, parameter.PureGoType) + if (parser.UseWasm() && strings.Contains(alloc, "js.TypedArrayOf(")) || GoType(function, parameter.Value, parameter.PureGoType) == "*bool" { + input = append(input, fmt.Sprintf("%vC", parser.CleanName(parameter.Name, parameter.Value))) + } else { + input = append(input, alloc) + } + } + } + } + + return strings.Join(input, ", ") +} + +func GoInputParametersForJSAlloc(function *parser.Function) []string { + + input := make([]string, 0) + + if function.SignalMode == "" { + for _, parameter := range function.Parameters { + var ( + alloc = GoInputJS(parameter.Name, parameter.Value, function, parameter.PureGoType) + name = fmt.Sprintf("%vC", parser.CleanName(parameter.Name, parameter.Value)) + ) + switch goType(function, parameter.Value, parameter.PureGoType) { + case "string": + { + if !parser.UseWasm() { + continue + } + //TODO: make it possible to pass nil strings; fix this on C side instead + input = append(input, fmt.Sprintf("var %v js.Value\nif %v != \"\" || true {\n%v = %v\ndefer (*js.TypedArray)(unsafe.Pointer(uintptr(%v.Get(\"data_ptr\").Int()))).Release()\n}\n", name, parser.CleanName(parameter.Name, parameter.Value), name, alloc, name)) + } + + case "*string", "[]string", "error": + { + if !parser.UseWasm() { + continue + } + input = append(input, fmt.Sprintf("%v := %v\ndefer (*js.TypedArray)(unsafe.Pointer(uintptr(%v.Get(\"data_ptr\").Int()))).Release()\n", name, alloc, name)) + } + + case "*bool": + { + input = append(input, fmt.Sprintf("%v := qt.WASM.Call(\"_malloc\", 1)\nqt.WASM.Call(\"setValue\", %v, qt.GoBoolToInt(*%v), \"i8\")\ndefer func(){*%v = int8(qt.WASM.Call(\"getValue\", %v, \"i8\").Int()) != 0\nqt.WASM.Call(\"_free\", %v)\n}()\n", name, name, parser.CleanName(parameter.Name, parameter.Value), parser.CleanName(parameter.Name, parameter.Value), name, name)) + } + } + } + } + + return input +} + +func GoInputParametersForCAlloc(function *parser.Function) []string { + + input := make([]string, 0) + + if function.SignalMode == "" { + for _, parameter := range function.Parameters { + var ( + alloc = GoInput(parameter.Name, parameter.Value, function, parameter.PureGoType) + name = fmt.Sprintf("%vC", parser.CleanName(parameter.Name, parameter.Value)) + ) + switch goType(function, parameter.Value, parameter.PureGoType) { + case "string": + { + input = append(input, fmt.Sprintf("var %v *C.char\nif %v != \"\" {\n%v = %v\ndefer C.free(unsafe.Pointer(%v))\n}\n", name, parser.CleanName(parameter.Name, parameter.Value), name, alloc, name)) + } + + case "[]byte": + { + input = append(input, fmt.Sprintf("var %v *C.char\nif len(%v) != 0 {\n%v = %v\n}\n", name, parser.CleanName(parameter.Name, parameter.Value), name, alloc)) + } + + case "*string", "[]string", "error": + { + input = append(input, fmt.Sprintf("%v := %v\ndefer C.free(unsafe.Pointer(%v))\n", name, alloc, name)) + } + + case "*bool": + { + input = append(input, fmt.Sprintf("%v := %v\ndefer func(){*%v = %v}()\n", name, alloc, parser.CleanName(parameter.Name, parameter.Value), goOutput(name, parameter.Value, function, parameter.PureGoType))) + } + } + } + } + + return input +} + +func GoInputParametersForCallback(function *parser.Function) string { + + input := make([]string, len(function.Parameters)) + + for i, parameter := range function.Parameters { + if parameter.PureGoType != "" && !parser.IsBlackListedPureGoType(parameter.PureGoType) { + input[i] = fmt.Sprintf("%vD", parser.CleanName(parameter.Name, parameter.Value)) + } else { + if function.Name == "readData" && strings.HasPrefix(cgoOutput(parameter.Name, parameter.Value, function, parameter.PureGoType), "cGoUnpack") { + input[i] = "&retS" + } else if strings.Contains(goType(function, parameter.Value, parameter.PureGoType), "*bool") { + if function.SignalMode != parser.CALLBACK { + input[i] = "nil" //TODO: make *bool usable from pure js + } else { + input[i] = fmt.Sprintf("&%vR", parser.CleanName(parameter.Name, parameter.Value)) + } + } else { + input[i] = cgoOutput(parameter.Name, parameter.Value, function, parameter.PureGoType) + } + } + } + + return strings.Join(input, ", ") +} diff --git a/qt/tool-chain/binding/converter/body_output_cpp.go b/qt/tool-chain/binding/converter/body_output_cpp.go new file mode 100644 index 0000000..994b584 --- /dev/null +++ b/qt/tool-chain/binding/converter/body_output_cpp.go @@ -0,0 +1,110 @@ +package converter + +import ( + "fmt" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func CppOutputParameters(function *parser.Function, name string) string { + if function.Meta == parser.CONSTRUCTOR { + return CppOutput(name, function.Name, function) + } + return CppOutput(name, function.Output, function) +} + +func CppOutputParametersFailed(function *parser.Function) string { + var output = GoOutputParametersFromCFailed(function) + if output == "nil" { + output = "NULL" + } + return output +} + +func CppOutputParametersDeducedFromGeneric(function *parser.Function) string { + + if function.TemplateModeGo != "" { + return fmt.Sprintf("<%v>", function.TemplateModeGo) + } + + switch function.TemplateModeJNI { + case "Int": + { + return "" + } + + case "Boolean": + { + return "" + } + + case "Void": + { + return "" + } + + case "Object", "String": + { + if function.Name == "callObjectMethod" || function.Name == "callStaticObjectMethod" { + if function.OverloadNumber == "2" || function.OverloadNumber == "4" { + return "" + } + } + + return "" + } + } + + return "" +} + +func CppOutputParametersJNIGenericModes(function *parser.Function) []string { + + switch function.Name { + case "callMethod", "callStaticMethod": + { + return []string{"Int", "Boolean", "Void"} //TODO: more primitives + } + + case "getField", "setField", "getStaticField", "setStaticField": + { + return []string{"Int", "Boolean"} //TODO: more primitives + } + + case "getObjectField", "getStaticObjectField", "callObjectMethod", "callStaticObjectMethod": + { + return []string{"Object", "String"} //TODO: add []string, []int, []object, ... + } + } + + return make([]string, 0) +} + +func CppOutputTemplateJS(function *parser.Function) string { + out := parser.CleanValue(function.Output) + switch out { + case "char", "qint8", "uchar", "quint8", "GLubyte", "QString", "QStringList": + return "emscripten::val" + + case "longlong", "long long", "qlonglong", "qint64": + if function.BoundByEmscripten || function.SignalMode == parser.CALLBACK { + return "long" + } + + case "ulonglong", "unsigned long long", "qulonglong", "quint64": + if function.BoundByEmscripten || function.SignalMode == parser.CALLBACK { + return "unsigned long" + } + } + + switch { + case len(out) == 0: + return "void" + case isClass(out) || parser.IsPackedList(out) || parser.IsPackedMap(out) || cppType(function, function.Output) == "void*" || cppType(function, function.Output) == "uintptr_t": + return "uintptr_t" + case isEnum(function.ClassName(), out) && (function.BoundByEmscripten || function.SignalMode == parser.CALLBACK): + return "long" + default: + return out + } +} diff --git a/qt/tool-chain/binding/converter/body_output_go.go b/qt/tool-chain/binding/converter/body_output_go.go new file mode 100644 index 0000000..6770ca9 --- /dev/null +++ b/qt/tool-chain/binding/converter/body_output_go.go @@ -0,0 +1,24 @@ +package converter + +import "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + +func GoOutputParametersFromC(function *parser.Function, name string) string { + if function.Meta == parser.CONSTRUCTOR { + return goOutput(name, function.Name, function, function.PureGoOutput) + } + return goOutput(name, function.Output, function, function.PureGoOutput) +} + +func GoJSOutputParametersFromC(function *parser.Function, name string) string { + if function.Meta == parser.CONSTRUCTOR { + return goOutputJS(name, function.Name, function, function.PureGoOutput) + } + return goOutputJS(name, function.Output, function, function.PureGoOutput) +} + +func GoOutputParametersFromCFailed(function *parser.Function) string { + if function.Meta == parser.CONSTRUCTOR { + return goOutputFailed(function.Name, function, function.PureGoOutput) + } + return goOutputFailed(function.Output, function, function.PureGoOutput) +} diff --git a/qt/tool-chain/binding/converter/enum.go b/qt/tool-chain/binding/converter/enum.go new file mode 100644 index 0000000..0c782b3 --- /dev/null +++ b/qt/tool-chain/binding/converter/enum.go @@ -0,0 +1,45 @@ +package converter + +import ( + "fmt" + "strconv" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func EnumNeedsCppGlue(value string) bool { + return strings.ContainsAny(value, "()<>~+") || value == "" || value == "0x1FFFFFFFU" +} + +func GoEnum(n string, v string, e *parser.Enum) string { + var _, err = strconv.Atoi(v) + switch { + case EnumNeedsCppGlue(v): + { + e.NoConst = true + if parser.UseJs() { + if parser.UseWasm() { + return fmt.Sprintf("int64(qt.WASM.Call(\"_%v_%v_Type\").Int())", strings.Split(e.Fullname, "::")[0], n) + } + return fmt.Sprintf("qt.WASM.Call(\"_%v_%v_Type\").Int64()", strings.Split(e.Fullname, "::")[0], n) + } + return fmt.Sprintf("C.%v_%v_Type()", strings.Split(e.Fullname, "::")[0], n) + } + + case strings.Contains(v, "0x"): + { + return v + } + + case err != nil: + { + if c, ok := parser.State.ClassMap[class(goEnum(e, v))]; ok && module(c.Module) != module(e) && module(c.Module) != "" { + return fmt.Sprintf("%v.%v", module(c.Module), goEnum(e, v)) + } + return goEnum(e, v) + } + } + + return v +} diff --git a/qt/tool-chain/binding/converter/header.go b/qt/tool-chain/binding/converter/header.go new file mode 100644 index 0000000..1c593b1 --- /dev/null +++ b/qt/tool-chain/binding/converter/header.go @@ -0,0 +1,352 @@ +package converter + +import ( + "bytes" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func GoHeaderName(f *parser.Function) string { + + if f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("callback%v_%v%v", f.ClassName(), strings.Replace(strings.Title(f.Name), parser.TILDE, "Destroy", -1), f.OverloadNumber) + } + + var bb = new(bytes.Buffer) + defer bb.Reset() + + if f.Static { + fmt.Fprintf(bb, "%v_", strings.Split(f.Fullname, "::")[0]) + } + + fmt.Fprint(bb, f.SignalMode) + + switch { + case f.Meta == parser.CONSTRUCTOR: + { + fmt.Fprint(bb, "New") + } + + case f.Meta == parser.DESTRUCTOR, strings.HasPrefix(f.Name, parser.TILDE): + { + fmt.Fprint(bb, "Destroy") + } + } + + switch f.TemplateModeJNI { + case "String", "Object": + { + if strings.Contains(f.Name, "Object") { + if f.TemplateModeJNI == "String" { + fmt.Fprintf(bb, "%v%v", strings.Replace(strings.Title(f.Name), "Object", "", -1), f.TemplateModeJNI) + } else { + fmt.Fprint(bb, strings.Title(f.Name)) + } + } + } + + default: + { + fmt.Fprintf(bb, "%v%v", + + func() string { + if strings.HasSuffix(f.Name, "_atList") || strings.HasSuffix(f.Name, "_setList") || + strings.HasSuffix(f.Name, "_newList") || strings.HasSuffix(f.Name, "_keyList") { + return f.Name + } + return strings.Title(f.Name) + }(), + + f.TemplateModeJNI, + ) + } + } + + if f.Overload { + fmt.Fprint(bb, f.OverloadNumber) + } + + if f.Default { + fmt.Fprint(bb, "Default") + } + + if f.Exception { + fmt.Fprint(bb, "Caught") + } + + if strings.ContainsAny(bb.String(), "&<>=/!()[]{}^|*+-") || strings.Contains(bb.String(), "Operator") { + f.Access = "unsupported_GoHeaderName" + return f.Access + } + + return strings.Replace(bb.String(), parser.TILDE, "", -1) +} + +func CppHeaderName(f *parser.Function) string { + return fmt.Sprintf("%v_%v", f.ClassName(), GoHeaderName(f)) +} + +func GoHeaderOutput(f *parser.Function) string { + + switch f.SignalMode { + case parser.CALLBACK: + { + if parser.UseJs() { + if parser.UseWasm() { + return "interface{}" + } + cv := parser.CleanValue(f.Output) + switch cv { + case "char", "qint8", "uchar", "quint8", "GLubyte", "QString", "QStringList": + return "*js.Object" + } + if isClass(cv) || parser.IsPackedList(cv) || parser.IsPackedMap(cv) || goType(f, f.Output, f.PureGoOutput) == "unsafe.Pointer" { + return "uintptr" + } + return goType(f, f.Output, f.PureGoOutput) + } + return cgoTypeOutput(f, f.Output) + } + + case parser.CONNECT, parser.DISCONNECT: + { + return "" + } + } + + if f.PureGoOutput != "" && !parser.IsBlackListedPureGoType(f.PureGoOutput) { + return f.PureGoOutput + } + + var value = f.Output + + if f.Meta == parser.CONSTRUCTOR && f.Output == "" { + value = f.Name + } + + var o = goType(f, value, f.PureGoOutput) + if isClass(o) { + if !strings.HasPrefix(o, "[]") && !strings.HasPrefix(o, "map[") { + o = fmt.Sprintf("*%v", o) + } + } + + if f.Exception { + if o != "" { + o += ", " + } + o += "error" + o = fmt.Sprintf("(%v)", o) + } + return o +} + +func CppHeaderOutput(f *parser.Function) string { + + var value = f.Output + + if f.Meta == parser.CONSTRUCTOR && f.Output == "" { + value = f.Name + } + + return cppType(f, value) +} + +func GoHeaderInput(f *parser.Function) string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + if f.SignalMode == parser.DISCONNECT { + return bb.String() + } + + if f.SignalMode == parser.CALLBACK { + if parser.UseJs() { + if parser.UseWasm() { + return "_ js.Value, args []js.Value" + } + fmt.Fprint(bb, "ptr uintptr") + } else { + fmt.Fprint(bb, "ptr unsafe.Pointer") + } + for _, p := range f.Parameters { + if parser.UseJs() { //TODO: move into goType ? + if v := goType(f, p.Value, p.PureGoType); v != "" { + cv := parser.CleanValue(p.Value) + if isEnum(f.ClassName(), cv) { + fmt.Fprintf(bb, ", %v int64", parser.CleanName(p.Name, p.Value)) + } else if isClass(cv) { + if cv == "QString" || cv == "QStringList" { + if f.FakeForJSCallback { + fmt.Fprintf(bb, ", %v string", parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, ", %vP *js.Object", parser.CleanName(p.Name, p.Value)) + } + } else { + if f.FakeForJSCallback { + fmt.Fprintf(bb, ", %v *js.Object", parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, ", %v uintptr", parser.CleanName(p.Name, p.Value)) + } + } + } else { + if parser.IsPackedList(cv) || parser.IsPackedMap(cv) { + if parser.UseWasm() { + fmt.Fprintf(bb, ", %v js.Value", parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, ", %v *js.Object", parser.CleanName(p.Name, p.Value)) + } + } else { + if v == "string" { + if f.FakeForJSCallback { + fmt.Fprintf(bb, ", %v string", parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, ", %vP *js.Object", parser.CleanName(p.Name, p.Value)) + } + } else { + if v == "*bool" { + fmt.Fprintf(bb, ", %v uintptr", parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, ", %v %v", parser.CleanName(p.Name, p.Value), v) + } + } + } + } + } + } else { + if v := cgoType(f, p.Value); v != "" { + fmt.Fprintf(bb, ", %v %v", parser.CleanName(p.Name, p.Value), v) + } + } + } + return bb.String() + } + + if f.SignalMode == parser.CONNECT { + fmt.Fprint(bb, "f func (") + } + + if (f.Meta == parser.SIGNAL || strings.Contains(f.Virtual, parser.IMPURE)) && f.SignalMode != parser.CONNECT { + if strings.Contains(f.Virtual, parser.IMPURE) && f.SignalMode == "" { + } else { + return bb.String() + } + } + + var tmp = make([]string, 0) + for _, p := range f.Parameters { + if p.PureGoType != "" && !parser.IsBlackListedPureGoType(p.PureGoType) { + tmp = append(tmp, fmt.Sprintf("%v %v", parser.CleanName(p.Name, p.Value), p.PureGoType)) + } else { + if v := goType(f, p.Value, p.PureGoType); v != "" { + if isClass(v) && !parser.IsPackedList(parser.CleanValue(p.Value)) && !parser.IsPackedMap(parser.CleanValue(p.Value)) { + if f.SignalMode == parser.CONNECT { + tmp = append(tmp, fmt.Sprintf("%v *%v", parser.CleanName(p.Name, p.Value), v)) + } else { + tmp = append(tmp, fmt.Sprintf("%v %v_ITF", parser.CleanName(p.Name, p.Value), v)) + } + } else { + tmp = append(tmp, fmt.Sprintf("%v %v", parser.CleanName(p.Name, p.Value), v)) + } + } else { + f.Access = "unsupported_GoHeaderInput" + return f.Access + } + } + } + fmt.Fprint(bb, strings.Join(tmp, ", ")) + + if f.SignalMode == parser.CONNECT { + fmt.Fprint(bb, ")") + + if f.PureGoOutput != "" && !parser.IsBlackListedPureGoType(f.PureGoOutput) { + fmt.Fprintf(bb, " %v", f.PureGoOutput) + } else { + if isClass(goType(f, f.Output, f.PureGoOutput)) && !parser.IsPackedList(parser.CleanValue(f.Output)) && !parser.IsPackedMap(parser.CleanValue(f.Output)) { + fmt.Fprintf(bb, " *%v", goType(f, f.Output, f.PureGoOutput)) + } else { + fmt.Fprintf(bb, " %v", goType(f, f.Output, f.PureGoOutput)) + } + } + } + + return bb.String() +} + +//TODO: combine with above +func GoHeaderInputSignalFunction(f *parser.Function) string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + fmt.Fprint(bb, "func (") + + var tmp = make([]string, 0) + + for _, p := range f.Parameters { + if p.PureGoType != "" && !parser.IsBlackListedPureGoType(p.PureGoType) { + tmp = append(tmp, fmt.Sprintf("%v", p.PureGoType)) + } else { + if v := goType(f, p.Value, p.PureGoType); v != "" { + if isClass(v) && !parser.IsPackedList(parser.CleanValue(p.Value)) && !parser.IsPackedMap(parser.CleanValue(p.Value)) { + tmp = append(tmp, fmt.Sprintf("*%v", v)) + } else { + tmp = append(tmp, v) + } + } else { + f.Access = "unsupported_GoHeaderInputSignalFunction" + return f.Access + } + } + } + + fmt.Fprint(bb, strings.Join(tmp, ", ")) + + fmt.Fprint(bb, ")") + + if f.SignalMode == parser.CALLBACK { + if f.PureGoOutput != "" && !parser.IsBlackListedPureGoType(f.PureGoOutput) { + fmt.Fprintf(bb, " %v", f.PureGoOutput) + } else { + if isClass(goType(f, f.Output, f.PureGoOutput)) && !parser.IsPackedList(parser.CleanValue(f.Output)) && !parser.IsPackedMap(parser.CleanValue(f.Output)) { + fmt.Fprintf(bb, " *%v", goType(f, f.Output, f.PureGoOutput)) + } else { + fmt.Fprintf(bb, " %v", goType(f, f.Output, f.PureGoOutput)) + } + } + } + + return bb.String() +} + +func GoGoInput(f *parser.Function) string { + var tmp = make([]string, 0) + for _, p := range f.Parameters { + tmp = append(tmp, parser.CleanName(p.Name, p.Value)) + } + return strings.Join(tmp, ", ") +} + +func CppHeaderInput(f *parser.Function) string { + var tmp = make([]string, 0) + + if !(f.Static || f.Meta == parser.CONSTRUCTOR) { + tmp = append(tmp, "void* ptr") + } + + if f.Meta == parser.SIGNAL { + return strings.Join(tmp, ", ") + } + + for _, p := range f.Parameters { + if v := cppTypeInput(f, p.Value); v != "" { + tmp = append(tmp, fmt.Sprintf("%v %v", v, parser.CleanName(p.Name, p.Value))) + } else { + f.Access = "unsupported_CppHeaderInput" + return f.Access + } + } + + return strings.Join(tmp, ", ") +} diff --git a/qt/tool-chain/binding/converter/helper.go b/qt/tool-chain/binding/converter/helper.go new file mode 100644 index 0000000..830bf83 --- /dev/null +++ b/qt/tool-chain/binding/converter/helper.go @@ -0,0 +1,304 @@ +package converter + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func module(input interface{}) string { + + switch input.(type) { + case *parser.Enum, *parser.Function: + { + return module(parser.State.ClassMap[class(input)].Module) + } + + case string: + { + return strings.ToLower(strings.TrimPrefix(input.(string), "Qt")) + } + } + return "" +} + +func class(input interface{}) string { + + switch input.(type) { + case *parser.Function: + { + return class(input.(*parser.Function).Fullname) + } + + case *parser.Enum: + { + return class(input.(*parser.Enum).Fullname) + } + + case string: + { + if strings.Contains(input.(string), "::") { + return strings.Split(input.(string), "::")[0] + } + if strings.Contains(input.(string), "__") { + return strings.Split(input.(string), "__")[0] + } + } + } + + return "" +} + +func isClass(value string) bool { + _, ok := parser.IsClass(value) + return ok +} + +func isEnum(class, value string) bool { + outE, _ := findEnum(class, value, false) + return outE != "" +} + +func findEnum(className, value string, byValue bool) (string, string) { + + //look in given class + if c, ok := parser.State.ClassMap[class(value)]; ok { + for _, e := range c.Enums { + if outE, outT := findEnumH(e, value, byValue); outE != "" { + return outE, outT + } + } + } + + //look in same class + if c, ok := parser.State.ClassMap[className]; ok { + for _, e := range c.Enums { + if outE, outT := findEnumH(e, value, byValue); outE != "" { + return outE, outT + } + } + } + + //look in super classes + if c, ok := parser.State.ClassMap[className]; ok { + for _, s := range c.GetAllBases() { + if sc, ok := parser.State.ClassMap[s]; ok { + for _, e := range sc.Enums { + if outE, outT := findEnumH(e, value, byValue); outE != "" { + return outE, outT + } + } + } + } + } + + return "", "" +} + +func findEnumH(e *parser.Enum, value string, byValue bool) (string, string) { + + if byValue { + for _, v := range e.Values { + if outE, _ := findEnumHelper(value, fmt.Sprintf("%v::%v", class(e), v.Name), ""); outE != "" { + return outE, "" + } + } + } else { + if outE, outT := findEnumHelper(value, e.Fullname, e.Typedef); outE != "" { + return outE, outT + } + } + + return "", "" +} + +func findEnumHelper(value, name, typedef string) (string, string) { + + var fullName = name + + if strings.Contains(value, "::") { + value = strings.Split(value, "::")[1] + } + + if strings.Contains(name, "::") { + name = strings.Split(name, "::")[1] + } + + if strings.Contains(typedef, "::") { + typedef = strings.Split(typedef, "::")[1] + } + + switch value { + case name, typedef: + { + return fullName, typedef + } + } + return "", "" +} + +func goEnum(inter interface{}, value string) string { + + var findByValue bool + + switch inter.(type) { + case *parser.Enum: + { + findByValue = true + } + } + + if outE, _ := findEnum(class(inter), value, findByValue); outE != "" { + return strings.Replace(outE, ":", "_", -1) + } + + switch deduced := inter.(type) { + case *parser.Function: + { + deduced.Access = "unsupported_goEnum" + } + + case *parser.Enum: + { + deduced.Access = "unsupported_goEnum" + } + } + + return "unsupported_goEnum" +} + +func cppEnum(f *parser.Function, value string, exact bool) string { + + if outE, outT := findEnum(class(f), value, false); outE != "" { + if exact { + + if outT == "" { + return outE + } + + if !strings.Contains(outT, "::") { + outT = fmt.Sprintf("%v::%v", class(outE), outT) + } + + return cppEnumExact(value, outE, outT) + } + + return outE + } + + f.Access = fmt.Sprintf("unsupported_cppEnum(%v)", value) + return f.Access +} + +func cppEnumExact(value, outE, outT string) string { + var trimedValue = value + + if strings.Contains(value, "::") { + trimedValue = strings.Split(value, "::")[1] + } + + if trimedValue == strings.Split(outT, "::")[1] { + return outT + } + return outE +} + +func IsPrivateSignal(f *parser.Function) bool { + var fc, ok = f.Class() + if !ok { + return false + } + + if fc.Module == "QtCore" { + + var ( + fData string + fPath = strings.Replace(filepath.Base(f.Filepath), ".cpp", ".h", -1) + ) + fPath = strings.Replace(fPath, ".mm", ".h", -1) + + if strings.HasSuffix(fPath, "_win.h") { + fPath = strings.Replace(fPath, "_win.h", ".h", -1) + } + + switch runtime.GOOS { + case "darwin": + { + if utils.QT_HOMEBREW() || utils.QT_MACPORTS() { + fData = utils.LoadOptional(filepath.Join(utils.QT_DARWIN_DIR(), "lib", fmt.Sprintf("%v.framework", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule)), "Versions", "5", "Headers", fPath)) + } else if utils.QT_NIX() { + for _, qmakepath := range strings.Split(os.Getenv("QMAKEPATH"), string(filepath.ListSeparator)) { + if strings.Contains(qmakepath, "qtbase") { + fData = utils.Load(filepath.Join(qmakepath, "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath)) + break + } + } + } else { + fData = utils.LoadOptional(filepath.Join(utils.QT_DARWIN_DIR(), "lib", fmt.Sprintf("%v.framework", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule)), "Versions", "5", "Headers", fPath)) + if len(fData) == 0 { + fData = utils.LoadOptional(filepath.Join(utils.QT_DARWIN_DIR(), "lib", fmt.Sprintf("%v.framework", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule)), "Headers", fPath)) + } + } + } + + case "windows": + { + if utils.QT_MSYS2() { + if utils.QT_MSYS2_STATIC() { + fData = utils.LoadOptional(filepath.Join(utils.QT_MSYS2_DIR(), "qt5-static", "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath)) + } else { + fData = utils.LoadOptional(filepath.Join(utils.QT_MSYS2_DIR(), "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath)) + } + } else { + path := filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "mingw73_64", "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath) + if !utils.ExistsDir(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR())) { + path = filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "mingw73_64", "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath) + } + if !utils.ExistsFile(path) { + path = strings.Replace(path, "mingw73_64", "mingw53_32", -1) + } + if !utils.ExistsFile(path) { + path = strings.Replace(path, "mingw53_32", "mingw49_32", -1) + } + fData = utils.Load(path) + } + } + + case "linux": + { + switch { + case utils.QT_PKG_CONFIG(): + fData = utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmd(exec.Command("pkg-config", "--variable=includedir", "Qt5Core"), "convert.IsPrivateSignal_includeDir")), strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath)) + case utils.QT_SAILFISH(): + fData = utils.LoadOptional(filepath.Join("/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include/qt5", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath)) + default: + path := filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "gcc_64", "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath) + if !utils.ExistsDir(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR())) { + path = filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "gcc_64", "include", strings.Title(parser.State.ClassMap[f.ClassName()].DocModule), fPath) + } + fData = utils.Load(path) + } + } + } + + if fData != "" { + if strings.Contains(fData, fmt.Sprintf("%v(", f.Name)) { + return strings.Contains(strings.Split(strings.Split(fData, fmt.Sprintf("%v(", f.Name))[1], ");")[0], "QPrivateSignal") + } + + if strings.Contains(fData, fmt.Sprintf("%v (", f.Name)) { + return strings.Contains(strings.Split(strings.Split(fData, fmt.Sprintf("%v (", f.Name))[1], ");")[0], "QPrivateSignal") + } + } + + utils.Log.Debugln("converter.IsPrivateSignal", f.ClassName()) + } + + return false +} diff --git a/qt/tool-chain/binding/converter/input.go b/qt/tool-chain/binding/converter/input.go new file mode 100644 index 0000000..ab54076 --- /dev/null +++ b/qt/tool-chain/binding/converter/input.go @@ -0,0 +1,693 @@ +package converter + +//TODO: GLchar, GLbyte + +import ( + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func GoInput(name, value string, f *parser.Function, p string) string { + if parser.UseJs() { + return GoInputJS(name, value, f, p) + } + + var vOld = value + + name = parser.CleanName(name, value) + value = parser.CleanValue(value) + + switch value { + case "char", "qint8": + { + if strings.Contains(vOld, "**") { + return fmt.Sprintf("C.CString(strings.Join(%v, \"|\"))", name) + } + + if value == "char" && strings.Count(vOld, "*") == 1 && f.Name == "readData" { + return fmt.Sprintf("C.CString(strings.Repeat(\"0\", int(%v)))", parser.CleanName(f.Parameters[1].Name, f.Parameters[1].Value)) + } + + switch value { + case "char", "qint8": + if len(f.Parameters) <= 4 && + (strings.Contains(strings.ToLower(f.Name), "read") || + strings.Contains(strings.ToLower(f.Name), "write") || + strings.Contains(strings.ToLower(f.Name), "data")) { + for _, p := range f.Parameters { + if strings.Contains(p.Value, "int") && f.Parameters[0].Value == vOld { + return fmt.Sprintf("(*C.char)(unsafe.Pointer(&%v[0]))", name) + } + } + } + } + + return fmt.Sprintf("C.CString(%v)", name) + } + + case "uchar", "quint8", "GLubyte", "QString": + { + switch value { + case "uchar", "quint8", "GLubyte": + if len(f.Parameters) <= 4 && + (strings.Contains(strings.ToLower(f.Name), "read") || + strings.Contains(strings.ToLower(f.Name), "write") || + strings.Contains(strings.ToLower(f.Name), "data")) { + for _, p := range f.Parameters { + if strings.Contains(p.Value, "int") && f.Parameters[0].Value == vOld { + return fmt.Sprintf("(*C.char)(unsafe.Pointer(&%v[0]))", name) + } + } + } + } + + return fmt.Sprintf("C.CString(%v)", func() string { + if strings.Contains(p, "error") { + return fmt.Sprintf("func() string { tmp := %v\n if tmp != nil { return tmp.Error() }\n return \"\" }()", name) + } + return name + }()) + } + + case "QStringList": + { + return fmt.Sprintf("C.CString(strings.Join(%v, \"|\"))", name) + } + + case "void", "GLvoid" /*, ""*/ : + { + if strings.Contains(vOld, "*") { + return name + } + } + + case "bool", "GLboolean": + { + if strings.Contains(vOld, "*") { + return fmt.Sprintf("C.char(int8(qt.GoBoolToInt(*%v)))", name) + } + return fmt.Sprintf("C.char(int8(qt.GoBoolToInt(%v)))", name) + } + + case "short", "qint16", "GLshort": + { + return fmt.Sprintf("C.short(%v)", name) + } + + case "ushort", "unsigned short", "quint16", "GLushort": + { + return fmt.Sprintf("C.ushort(%v)", name) + } + + case "int", "qint32", "GLint", "GLsizei", "GLintptrARB", "GLsizeiptrARB", "GLfixed", "GLclampx": + { + return fmt.Sprintf("C.int(int32(%v))", name) + } + + case "uint", "unsigned int", "quint32", "GLenum", "GLbitfield", "GLuint", "QRgb": + { + return fmt.Sprintf("C.uint(uint32(%v))", name) + } + + case "long": + { + return fmt.Sprintf("C.long(int32(%v))", name) + } + + case "ulong", "unsigned long": + { + return fmt.Sprintf("C.ulong(uint32(%v))", name) + } + + case "longlong", "long long", "qlonglong", "qint64": + { + return fmt.Sprintf("C.longlong(%v)", name) + } + + case "ulonglong", "unsigned long long", "qulonglong", "quint64": + { + return fmt.Sprintf("C.ulonglong(%v)", name) + } + + case "float", "GLfloat", "GLclampf": + { + return fmt.Sprintf("C.float(%v)", name) + } + + case "double", "qreal": + { + if value == "qreal" && strings.HasPrefix(parser.State.Target, "sailfish") { + return fmt.Sprintf("C.float(%v)", name) + } + return fmt.Sprintf("C.double(%v)", name) + } + + case "uintptr_t", "uintptr", "quintptr", "WId": + { + return fmt.Sprintf("C.uintptr_t(%v)", name) + } + + //non std types + + case "T": + { + switch f.TemplateModeJNI { + case "Boolean": + { + return fmt.Sprintf("C.char(int8(qt.GoBoolToInt(%v)))", name) + } + + case "Int": + { + return fmt.Sprintf("C.int(int32(%v))", name) + } + } + + if module(f) == "androidextras" { + return "p0" + } + } + + case "JavaVM", "jclass", "jobject": + { + return name + } + + case "...": + { + var tmp = make([]string, 10) + for i := range tmp { + tmp[i] = fmt.Sprintf("p%v", i) + } + return strings.Join(tmp, ", ") + } + } + + switch { + case isEnum(f.ClassName(), value): + { + return fmt.Sprintf("C.longlong(%v)", name) + } + + case isClass(value): + { + if strings.Contains(value, ".") { + value = strings.Split(value, ".")[1] + } + if m := module(parser.State.ClassMap[value].Module); m != module(f) { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[parser.State.ClassMap[value].Module]; ok { + return name + } + return fmt.Sprintf("%v.PointerFrom%v(%v)", m, strings.Title(value), name) + } + return fmt.Sprintf("PointerFrom%v(%v)", strings.Title(value), name) + } + + case parser.IsPackedList(value): + { + if strings.ContainsAny(name, "*&()[]") { + return fmt.Sprintf("func() unsafe.Pointer {\ntmpList := New%vFromPointer(New%vFromPointer(nil).__%v_newList%v())\nfor _,v := range %v{\ntmpList.__%v_setList%v(v)\n}\nreturn tmpList.Pointer()\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, f.OverloadNumber, name, f.Name, f.OverloadNumber) + } + return fmt.Sprintf("func() unsafe.Pointer {\ntmpList := New%vFromPointer(New%vFromPointer(nil).__%v_%v_newList%v())\nfor _,v := range %v{\ntmpList.__%v_%v_setList%v(v)\n}\nreturn tmpList.Pointer()\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, name, f.OverloadNumber, name, f.Name, name, f.OverloadNumber) + } + + case parser.IsPackedMap(value): + { + if strings.ContainsAny(name, "*&()[]") { + return fmt.Sprintf("func() unsafe.Pointer {\ntmpList := New%vFromPointer(New%vFromPointer(nil).__%v_newList%v())\nfor k,v := range %v{\ntmpList.__%v_setList%v(k, v)\n}\nreturn tmpList.Pointer()\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, f.OverloadNumber, name, f.Name, f.OverloadNumber) + } + return fmt.Sprintf("func() unsafe.Pointer {\ntmpList := New%vFromPointer(New%vFromPointer(nil).__%v_%v_newList%v())\nfor k,v := range %v{\ntmpList.__%v_%v_setList%v(k, v)\n}\nreturn tmpList.Pointer()\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, name, f.OverloadNumber, name, f.Name, name, f.OverloadNumber) + } + } + + f.Access = fmt.Sprintf("unsupported_goInput(%v)", value) + return f.Access +} + +func CppInput(name, value string, f *parser.Function) string { + + if (f.SignalMode == parser.CALLBACK || strings.HasPrefix(name, "callback") || strings.HasPrefix(name, "emscripten::val::global")) && (parser.CleanValue(value) == "QString" || parser.CleanValue(value) == "QStringList") { + if parser.UseJs() { + if parser.UseWasm() { + return fmt.Sprintf("({ emscripten::val tempVal = %v; %v ret = %v; emscripten::val::global(\"Module\").call(\"_callbackReleaseTypedArray\", tempVal[\"data_ptr\"].as()); ret; })", name, parser.CleanValue(value), cppInput("tempVal", value, f)) + } + return fmt.Sprintf("({ emscripten::val tempVal = %v; %v ret = %v; ret; })", name, parser.CleanValue(value), cppInput("tempVal", value, f)) + } + return fmt.Sprintf("({ %v_PackedString tempVal = %v; %v ret = %v; free(tempVal.data); ret; })", strings.Title(parser.State.ClassMap[f.ClassName()].Module), name, parser.CleanValue(value), cppInput("tempVal", value, f)) + } + + out := cppInput(name, value, f) + + if parser.UseJs() { + if isEnum(f.ClassName(), parser.CleanValue(value)) { + out = strings.Replace(out, "static_cast", "enum_cast", -1) + } + } + + return out +} + +func cppInput(name, value string, f *parser.Function) string { + var vOld = value + + name = parser.CleanName(name, value) + value = parser.CleanValue(value) + + switch value { + case "char", "qint8": + { + if strings.Contains(vOld, "**") && name == "argv" { + return "argvs" + } + + if parser.UseJs() { + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("QByteArray::fromStdString(%v[\"data\"].as()).constData()", name) + } + return fmt.Sprintf("const_cast(QByteArray::fromStdString(%v[\"data\"].as()).constData())", name) + } + return fmt.Sprintf("*const_cast(QByteArray::fromStdString(%v[\"data\"].as()).constData())", name) + } + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast(%v)", value, name) + } + return name + } + + return fmt.Sprintf("*%v", name) + } + + case "uchar", "quint8", "GLubyte": + { + if parser.UseJs() { + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast(static_cast<%v*>(static_cast(const_cast(QByteArray::fromStdString(%v[\"data\"].as()).constData()))))", value, value, name) + } + return fmt.Sprintf("static_cast<%v*>(static_cast(const_cast(QByteArray::fromStdString(%v[\"data\"].as()).constData())))", value, name) + } + return fmt.Sprintf("*static_cast<%v*>(static_cast(const_cast(QByteArray::fromStdString(%v[\"data\"].as()).constData())))", value, name) + } + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast(static_cast<%v*>(static_cast(%v)))", value, value, name) + } + return fmt.Sprintf("static_cast<%v*>(static_cast(%v))", value, name) + } + return fmt.Sprintf("*static_cast<%v*>(static_cast(%v))", value, name) + } + + case "QString": + { + if strings.Contains(vOld, "*") { + if parser.UseJs() { + return fmt.Sprintf("new QString(QString::fromStdString(%v[\"data\"].as()))", name) + } + return fmt.Sprintf("new QString(QString::fromUtf8(%[1]v.data, %[1]v.len))", name) + } + + if strings.Contains(vOld, "&") && !strings.Contains(vOld, "const") { + return fmt.Sprintf("*(%v)", cppInput(name, "QString*", f)) + } + + if parser.UseJs() { + return fmt.Sprintf("QString::fromStdString(%v[\"data\"].as())", name) + } + return fmt.Sprintf("QString::fromUtf8(%[1]v.data, %[1]v.len)", name) + } + + case "QStringList": + { + if strings.Contains(vOld, "*") { + return fmt.Sprintf("new QStringList(%v)", cppInput(name, "QStringList", f)) + } + + if strings.Contains(vOld, "&") && !strings.Contains(vOld, "const") { + return fmt.Sprintf("*(%v)", cppInput(name, "QStringList*", f)) + } + + if parser.UseJs() { + return fmt.Sprintf("QString::fromStdString(%v[\"data\"].as()).split(\"|\", QString::SkipEmptyParts)", name) + } + return fmt.Sprintf("QString::fromUtf8(%[1]v.data, %[1]v.len).split(\"|\", QString::SkipEmptyParts)", name) + } + + case "void", "GLvoid" /*, ""*/ : + { + if strings.Count(vOld, "*") == 2 && !strings.Contains(vOld, "**") { + break + } + + if strings.Contains(vOld, "**") { + return fmt.Sprintf("&%v", name) + } + + if parser.UseJs() { + if strings.Contains(vOld, "*") { + return fmt.Sprintf("reinterpret_cast(%v)", name) + } + } + + if strings.Contains(vOld, "*") { + return name + } + } + + case "bool", "GLboolean": + { + if strings.Contains(vOld, "*") { + if parser.UseJs() && f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("reinterpret_cast(%v)", value, name) + } + return fmt.Sprintf("reinterpret_cast<%v*>(%v)", value, name) + } + return fmt.Sprintf("%v != 0", name) + } + + case + "short", "qint16", "GLshort", + "ushort", "unsigned short", "quint16", "GLushort", + + "int", "qint32", "GLint", "GLsizei", "GLintptrARB", "GLsizeiptrARB", "GLfixed", "GLclampx", + "uint", "unsigned int", "quint32", "GLenum", "GLbitfield", "GLuint", "QRgb", + + "long", + "ulong", "unsigned long", + + "longlong", "long long", "qlonglong", "qint64", + "ulonglong", "unsigned long long", "qulonglong", "quint64", + + "float", "GLfloat", "GLclampf", + "double", "qreal", + + "uintptr_t", "uintptr", "quintptr", "WId": + { + if parser.UseJs() && f.BoundByEmscripten { + switch value { + case "longlong", "long long", "qlonglong", "qint64", + "ulonglong", "unsigned long long", "qulonglong", "quint64": + f.BoundByEmscripten = false + name = fmt.Sprintf("enum_cast<%v>(%v)", cppType(f, value), name) + f.BoundByEmscripten = true + } + } + if strings.Contains(vOld, "&") && name == "argc" { + return "argcs" + } + + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast(&%v)", value, name) + } + return fmt.Sprintf("&%v", name) + } + + return name + } + + //non std types + + case "T": + { + switch f.TemplateModeJNI { + case "Boolean", "Int": + { + return name + } + } + + if module(f) == "androidextras" { + return fmt.Sprintf("static_cast(%v)", name) + } + } + + case "JavaVM", "jclass", "jobject": + { + return fmt.Sprintf("static_cast<%v>(%v)", value, name) + } + + case "...": + { + var tmp = make([]string, 10) + for i := range tmp { + if i == 9 { + tmp[i] = fmt.Sprintf("static_cast(%v)", name) + } else { + tmp[i] = fmt.Sprintf("static_cast(%v%v)", name, i) + } + } + return strings.Join(tmp, ", ") + } + } + + switch { + case isEnum(f.ClassName(), value): + { + if !strings.Contains(vOld, "*") { + if f.Meta == parser.SLOT && f.SignalMode == "" && value == "Qt::Alignment" { + return fmt.Sprintf("static_cast<%v>(static_cast<%v>(%v))", value, cppEnum(f, value, false), name) + } + return fmt.Sprintf("static_cast<%v>(%v)", cppEnum(f, value, false), name) + } + } + + case isClass(value): + { + if strings.Contains(value, ".") { + value = strings.Split(value, ".")[1] + } + if strings.Contains(vOld, "*") && strings.Contains(vOld, "&") { + break + } + + if parser.State.ClassMap[value].Fullname != "" { + value = parser.State.ClassMap[value].Fullname + } + + if strings.Contains(vOld, "*") { + return fmt.Sprintf("static_cast<%v*>(%v)", value, name) + } + return fmt.Sprintf("*static_cast<%v*>(%v)", value, name) + } + + case parser.IsPackedList(value) || parser.IsPackedMap(value): + { + if strings.HasSuffix(vOld, "*") { + return fmt.Sprintf("static_cast<%v*>(%v)", value, name) + } + + if strings.HasPrefix(vOld, "const") || f.Fullname == "QMacToolBar::setItems" || f.Fullname == "QMacToolBar::setAllowedItems" { + return fmt.Sprintf("*static_cast<%v*>(%v)", value, name) + } + + return fmt.Sprintf("({ %v* tmpP = static_cast<%v*>(%v); %v tmpV = *tmpP; tmpP->~%v(); free(tmpP); tmpV; })", parser.CleanValue(value), value, name, parser.CleanValue(value), strings.Split(parser.CleanValue(value), "<")[0]) + } + } + + f.Access = fmt.Sprintf("unsupported_cppInput(%v)", value) + return f.Access +} + +func GoInputJS(name, value string, f *parser.Function, p string) string { + var vOld = value + + name = parser.CleanName(name, value) + value = parser.CleanValue(value) + + switch value { + case "char", "qint8", "uchar", "quint8", "GLubyte", "QString": + { + if strings.Contains(p, "error") { + name = fmt.Sprintf("func() string {\ntmp := %v\nif tmp != nil { return tmp.Error() }\nreturn \"\"\n}()", name) + } + + if strings.Contains(vOld, "**") { + if parser.UseWasm() { + return fmt.Sprintf("func() js.Value {\ntmp := js.TypedArrayOf([]byte(strings.Join(%v, \"|\")))\nreturn js.ValueOf(map[string]interface{}{\"data\": tmp, \"data_ptr\": unsafe.Pointer(&tmp)})\n}()", name) + } + if f.SignalMode != parser.CALLBACK { + return fmt.Sprintf("func() *js.Object {\ntmp := new(js.Object)\nif js.InternalObject(%v).Get(\"$val\") == js.Undefined {\ntmp.Set(\"data\", []byte(js.InternalObject(%v).Call(\"join\", \"|\").String()))\n} else {\ntmp.Set(\"data\", []byte(strings.Join(%v, \"|\")))\n}\nreturn tmp\n}()", name, name, name) //needed for indirect exported pure js call -> can be ommited if build without js support + } + return fmt.Sprintf("func() *js.Object {\ntmp := new(js.Object)\ntmp.Set(\"data\", []byte(strings.Join(%v, \"|\")))\nreturn tmp\n}()", name) + } + + if value == "char" && strings.Count(vOld, "*") == 1 && f.Name == "readData" { + //TODO: + } + + if parser.UseWasm() { + return fmt.Sprintf("func() js.Value {\ntmp := js.TypedArrayOf([]byte(%v))\nreturn js.ValueOf(map[string]interface{}{\"data\": tmp, \"data_ptr\": unsafe.Pointer(&tmp)})\n}()", name) + } + return fmt.Sprintf("func() *js.Object {\ntmp := new(js.Object)\ntmp.Set(\"data\", []byte(%v))\nreturn tmp\n}()", name) + } + + case "QStringList": + { + if parser.UseWasm() { + return fmt.Sprintf("func() js.Value {\ntmp := js.TypedArrayOf([]byte(strings.Join(%v, \"|\")))\nreturn js.ValueOf(map[string]interface{}{\"data\": tmp, \"data_ptr\": unsafe.Pointer(&tmp)})\n}()", name) + } + if f.SignalMode != parser.CALLBACK { + return fmt.Sprintf("func() *js.Object {\ntmp := new(js.Object)\nif js.InternalObject(%v).Get(\"$val\") == js.Undefined {\ntmp.Set(\"data\", []byte(js.InternalObject(%v).Call(\"join\", \"|\").String()))\n} else {\ntmp.Set(\"data\", []byte(strings.Join(%v, \"|\")))\n}\nreturn tmp\n}()", name, name, name) //needed for indirect exported pure js call -> can be ommited if build without js support + } + return fmt.Sprintf("func() *js.Object {\ntmp := new(js.Object)\ntmp.Set(\"data\", []byte(strings.Join(%v, \"|\")))\nreturn tmp\n}()", name) + } + + case "void", "GLvoid", "": + { + if strings.Contains(vOld, "*") { + return fmt.Sprintf("uintptr(%v)", name) + } + return name + } + + case "bool", "GLboolean": + { + return fmt.Sprintf("%v", name) + } + + case "short", "qint16", "GLshort": + { + return fmt.Sprintf("int16(%v)", name) + } + + case "ushort", "unsigned short", "quint16", "GLushort": + { + return fmt.Sprintf("uint16(%v)", name) + } + + case "int", "qint32", "GLint", "GLsizei", "GLintptrARB", "GLsizeiptrARB", "GLfixed", "GLclampx": + { + return fmt.Sprintf("int(int32(%v))", name) + } + + case "uint", "unsigned int", "quint32", "GLenum", "GLbitfield", "GLuint", "QRgb": + { + return fmt.Sprintf("uint(uint32(%v))", name) + } + + case "long": + { + return fmt.Sprintf("int(int32(%v))", name) + } + + case "ulong", "unsigned long": + { + return fmt.Sprintf("uint(uint32(%v))", name) + } + + case "longlong", "long long", "qlonglong", "qint64": + { + return fmt.Sprintf("int64(%v)", name) + } + + case "ulonglong", "unsigned long long", "qulonglong", "quint64": + { + return fmt.Sprintf("uint64(%v)", name) + } + + case "float", "GLfloat", "GLclampf": + { + return fmt.Sprintf("float32(%v)", name) + } + + case "double", "qreal": + { + return fmt.Sprintf("float64(%v)", name) + } + + case "uintptr_t", "uintptr", "quintptr", "WId": + { + return fmt.Sprintf("uintptr(%v)", name) + } + + //non std types + + case "T", "JavaVM", "jclass", "jobject": + { + switch f.TemplateModeJNI { + case "Boolean": + { + return fmt.Sprintf("int8(%v) != 0", name) + } + + case "Int": + { + return fmt.Sprintf("int(int32(%v))", name) + } + + case "Void": + { + return name + } + } + + return fmt.Sprintf("unsafe.Pointer(%v)", name) + } + } + + switch { + case isEnum(f.ClassName(), value): + { + if c, ok := parser.State.ClassMap[class(cppEnum(f, value, false))]; ok && module(c.Module) != module(f) && module(c.Module) != "" { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[c.Module]; ok { + if parser.UseWasm() || f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("int64(%v)", name) + } else { + return fmt.Sprintf("func() int64 {\nif js.InternalObject(%v).Get(\"$val\") == js.Undefined {\nreturn int64(js.InternalObject(%v).Int64())\n}\nreturn int64(%v)\n}()", name, name, name) //needed for indirect exported pure js call -> can be ommited if build without js support + } + } + if parser.UseWasm() { + return fmt.Sprintf("int64(%v.%v(%v))", module(c.Module), goEnum(f, value), name) + } + if f.SignalMode != parser.CALLBACK { + return fmt.Sprintf("func() %[1]v.%[2]v {\nif js.InternalObject(%[3]v).Get(\"$val\") == js.Undefined {\nreturn %[1]v.%[2]v(js.InternalObject(%[3]v).Int64())\n}\nreturn %[1]v.%[2]v(%[3]v)\n}()", module(c.Module), goEnum(f, value), name) //needed for indirect exported pure js call -> can be ommited if build without js support + } + return fmt.Sprintf("%v.%v(%v)", module(c.Module), goEnum(f, value), name) + } + if parser.UseWasm() { + return fmt.Sprintf("int64(%v(%v))", goEnum(f, value), name) + } + if f.SignalMode != parser.CALLBACK { + return fmt.Sprintf("func() %[1]v {\nif js.InternalObject(%[2]v).Get(\"$val\") == js.Undefined {\nreturn %[1]v(js.InternalObject(%[2]v).Int64())\n}\nreturn %[1]v(%[2]v)\n}()", goEnum(f, value), name) //needed for indirect exported pure js call -> can be ommited if build without js support + } + return fmt.Sprintf("%v(%v)", goEnum(f, value), name) + } + + case isClass(value): + { + if strings.Contains(value, ".") { + value = strings.Split(value, ".")[1] + } + if m := module(parser.State.ClassMap[value].Module); m != module(f) { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[parser.State.ClassMap[value].Module]; ok { + return fmt.Sprintf("uintptr(unsafe.Pointer(%v))", name) + } + return fmt.Sprintf("uintptr(%v.PointerFrom%v(%v))", m, strings.Title(value), name) + } + return fmt.Sprintf("uintptr(PointerFrom%v(%v))", strings.Title(value), name) + } + + case parser.IsPackedList(value): + { + if strings.ContainsAny(name, "*&()[]") { + return fmt.Sprintf("func() uintptr {\ntmpList := New%vFromPointer(unsafe.Pointer(New%vFromPointer(nil).__%v_newList%v()))\nfor _,v := range %v{\ntmpList.__%v_setList%v(v)\n}\nreturn uintptr(tmpList.Pointer())\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, f.OverloadNumber, name, f.Name, f.OverloadNumber) + } + return fmt.Sprintf("func() uintptr {\ntmpList := New%vFromPointer(unsafe.Pointer(New%vFromPointer(nil).__%v_%v_newList%v()))\nfor _,v := range %v{\ntmpList.__%v_%v_setList%v(v)\n}\nreturn uintptr(tmpList.Pointer())\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, name, f.OverloadNumber, name, f.Name, name, f.OverloadNumber) + } + + case parser.IsPackedMap(value): + { + if strings.ContainsAny(name, "*&()[]") { + return fmt.Sprintf("func() uintptr {\ntmpList := New%vFromPointer(unsafe.Pointer(New%vFromPointer(nil).__%v_newList%v()))\nfor k,v := range %v{\ntmpList.__%v_setList%v(k, v)\n}\nreturn uintptr(tmpList.Pointer())\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, f.OverloadNumber, name, f.Name, f.OverloadNumber) + } + return fmt.Sprintf("func() uintptr {\ntmpList := New%vFromPointer(unsafe.Pointer(New%vFromPointer(nil).__%v_%v_newList%v()))\nfor k,v := range %v{\ntmpList.__%v_%v_setList%v(k, v)\n}\nreturn uintptr(tmpList.Pointer())\n}()", strings.Title(f.ClassName()), strings.Title(f.ClassName()), f.Name, name, f.OverloadNumber, name, f.Name, name, f.OverloadNumber) + } + } + + f.Access = fmt.Sprintf("unsupported_goInputJS(%v)", value) + return f.Access +} diff --git a/qt/tool-chain/binding/converter/output.go b/qt/tool-chain/binding/converter/output.go new file mode 100644 index 0000000..fffc15c --- /dev/null +++ b/qt/tool-chain/binding/converter/output.go @@ -0,0 +1,1156 @@ +package converter + +//TODO: GLchar, GLbyte + +import ( + "crypto/sha1" + "encoding/hex" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func GoOutput(name, value string, f *parser.Function, p string) string { + return goOutput(name, value, f, p) +} +func goOutput(name, value string, f *parser.Function, p string) string { + vOld := value + + name = parser.CleanName(name, value) + value = parser.CleanValue(value) + + switch value { + case "char", "qint8", "uchar", "quint8", "GLubyte", "QString": + { + if !parser.UseJs() { //TODO: support []byte in js as well + switch value { + case "char", "qint8", "uchar", "quint8", "GLubyte": + if len(f.Parameters) <= 4 && + (strings.Contains(strings.ToLower(f.Name), "read") || + strings.Contains(strings.ToLower(f.Name), "write") || + strings.Contains(strings.ToLower(f.Name), "data")) { + for _, p := range f.Parameters { + if strings.Contains(p.Value, "int") && f.Parameters[0].Value == vOld { + return fmt.Sprintf("cGoUnpackBytes(%v)", name) + } + } + } + } + } + + return func() string { + var out = fmt.Sprintf("cGoUnpackString(%v)", name) + if strings.Contains(p, "error") { + return fmt.Sprintf("errors.New(%v)", out) + } + return out + }() + } + + case "QStringList": + { + return fmt.Sprintf("strings.Split(cGoUnpackString(%v), \"|\")", name) + } + + case "void", "GLvoid", "": + { + return name + } + + case "bool", "GLboolean": + { + return fmt.Sprintf("int8(%v) != 0", name) + } + + case "short", "qint16", "GLshort": + { + return fmt.Sprintf("int16(%v)", name) + } + + case "ushort", "unsigned short", "quint16", "GLushort": + { + return fmt.Sprintf("uint16(%v)", name) + } + + case "int", "qint32", "GLint", "GLsizei", "GLintptrARB", "GLsizeiptrARB", "GLfixed", "GLclampx": + { + return fmt.Sprintf("int(int32(%v))", name) + } + + case "uint", "unsigned int", "quint32", "GLenum", "GLbitfield", "GLuint", "QRgb": + { + return fmt.Sprintf("uint(uint32(%v))", name) + } + + case "long": + { + return fmt.Sprintf("int(int32(%v))", name) + } + + case "ulong", "unsigned long": + { + return fmt.Sprintf("uint(uint32(%v))", name) + } + + case "longlong", "long long", "qlonglong", "qint64": + { + return fmt.Sprintf("int64(%v)", name) + } + + case "ulonglong", "unsigned long long", "qulonglong", "quint64": + { + return fmt.Sprintf("uint64(%v)", name) + } + + case "float", "GLfloat", "GLclampf": + { + return fmt.Sprintf("float32(%v)", name) + } + + case "double", "qreal": + { + return fmt.Sprintf("float64(%v)", name) + } + + case "uintptr_t", "uintptr", "quintptr", "WId": + { + return fmt.Sprintf("uintptr(%v)", name) + } + + //non std types + + case "T", "JavaVM", "jclass", "jobject": + { + switch f.TemplateModeJNI { + case "Boolean": + { + return fmt.Sprintf("int8(%v) != 0", name) + } + + case "Int": + { + return fmt.Sprintf("int(int32(%v))", name) + } + + case "Void": + { + return name + } + } + + return fmt.Sprintf("unsafe.Pointer(%v)", name) + } + } + + switch { + case isEnum(f.ClassName(), value): + { + if c, ok := parser.State.ClassMap[class(cppEnum(f, value, false))]; ok && module(c.Module) != module(f) && module(c.Module) != "" { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[c.Module]; ok { + return fmt.Sprintf("int64(%v)", name) + } + return fmt.Sprintf("%v.%v(%v)", module(c.Module), goEnum(f, value), name) + } + return fmt.Sprintf("%v(%v)", goEnum(f, value), name) + } + + case isClass(value): + { + if strings.Contains(value, ".") { + value = strings.Split(value, ".")[1] + } + if m := module(parser.State.ClassMap[value].Module); m != module(f) { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[parser.State.ClassMap[value].Module]; ok { + return fmt.Sprintf("unsafe.Pointer(%v)", name) + } + return fmt.Sprintf("%v.New%vFromPointer(%v)", m, strings.Title(value), name) + } + return fmt.Sprintf("New%vFromPointer(%v)", strings.Title(value), name) + } + + case parser.IsPackedList(value): + { + return fmt.Sprintf("func(l C.struct_%v_PackedList)%v{out := make(%v, int(l.len))\ntmpList := New%vFromPointer(l.data)\nfor i:=0;ivalue%v; if (i == %v->size()-1) { %v->~%v(); free(reinterpret_cast(ptr)); }; tmp; })", value, strings.Split(name, "->")[0], "("+strings.TrimSuffix(strings.Split(name, "_atList(")[1], ", i)")+")", strings.Split(name, "->")[0], strings.Split(name, "->")[0], parser.CleanValue(f.Container)), value, f) + if !strings.Contains(cppOutput(name, value, f), "emscripten::val") && f.BoundByEmscripten { + if !strings.Contains(out, "emscripten::val::global") { + out = "reinterpret_cast(" + out + ")" + } + } + return out + } + out := cppOutput(fmt.Sprintf("({%v tmp = %v->at%v; if (i == %v->size()-1) { %v->~%v(); free(reinterpret_cast(ptr)); }; tmp; })", value, strings.Split(name, "->")[0], "("+strings.Split(name, "_atList(")[1], strings.Split(name, "->")[0], strings.Split(name, "->")[0], parser.CleanValue(f.Container)), value, f) + if !strings.Contains(cppOutput(name, value, f), "emscripten::val") && f.BoundByEmscripten { + if !strings.Contains(out, "emscripten::val::global") { + out = "reinterpret_cast(" + out + ")" + } + } + return out + } + if f.IsMap { + return cppOutput(fmt.Sprintf("({%v tmp = %v->value%v; if (i == %v->size()-1) { %v->~%v(); free(ptr); }; tmp; })", value, strings.Split(name, "->")[0], "("+strings.TrimSuffix(strings.Split(name, "_atList(")[1], ", i)")+")", strings.Split(name, "->")[0], strings.Split(name, "->")[0], parser.CleanValue(f.Container)), value, f) + } + return cppOutput(fmt.Sprintf("({%v tmp = %v->at%v; if (i == %v->size()-1) { %v->~%v(); free(ptr); }; tmp; })", value, strings.Split(name, "->")[0], "("+strings.Split(name, "_atList(")[1], strings.Split(name, "->")[0], strings.Split(name, "->")[0], parser.CleanValue(f.Container)), value, f) + } + if strings.HasSuffix(f.Name, "_setList") { + if len(f.Parameters) == 2 { + return cppOutput(fmt.Sprintf("%v->insert%v", strings.Split(name, "->")[0], "("+strings.Split(name, "_setList(")[1]), value, f) + } + return cppOutput(fmt.Sprintf("%v->append%v", strings.Split(name, "->")[0], "("+strings.Split(name, "_setList(")[1]), value, f) + } + if strings.HasSuffix(f.Name, "_newList") { + return fmt.Sprintf("new %v()", parser.CleanValue(f.Container)) + } + if strings.HasSuffix(f.Name, "_keyList") { + return cppOutput(fmt.Sprintf("static_cast<%v*>(ptr)->keys()", f.Container), value, f) + } + out := cppOutput(name, value, f) + + if f.BoundByEmscripten && (strings.Contains(CppHeaderOutput(f), "uintptr_t") || strings.Contains(CppHeaderOutput(f), "void*")) && f.SignalMode != parser.CALLBACK { + return fmt.Sprintf("reinterpret_cast(%v)", out) + } + + if parser.UseJs() && f.SignalMode != parser.CALLBACK { + for _, p := range f.Parameters { + if strings.Contains(cppType(f, p.Value), "emscripten::val") && isClass(parser.CleanValue(f.Output)) && !strings.Contains(cppType(f, f.Output), "emscripten::val") && f.BoundByEmscripten { + if !strings.Contains(out, "emscripten::val::global") && !strings.ContainsAny(out, ";") { + return fmt.Sprintf("reinterpret_cast(%v)", out) + } + } + } + } + + if parser.UseJs() && f.SignalMode != parser.CALLBACK { + if isClass(parser.CleanValue(f.Output)) && !strings.Contains(cppType(f, f.Output), "emscripten::val") && f.BoundByEmscripten { + if !strings.Contains(out, "emscripten::val::global") { + if strings.Contains(out, "; new") { + return strings.Replace(strings.Replace(out, "; new", "; reinterpret_cast(new", -1), "; })", "); })", -1) + } + } + } + } + + return out +} + +func cppOutputPack(name, value string, f *parser.Function) string { + var out = CppOutput(name, value, f) + + if strings.Contains(out, "_PackedString") { + var out = strings.Replace(out, "({ ", "", -1) + out = strings.Replace(out, " })", "", -1) + if !strings.HasSuffix(out, ";") { + out = fmt.Sprintf("%v;", out) + } + return strings.Replace(out, "_PackedString", fmt.Sprintf("_PackedString %vPacked =", parser.CleanName(name, value)), -1) + } + + return "" +} + +func cppOutputPacked(name, value string, f *parser.Function) string { + var out = CppOutput(name, value, f) + + if parser.UseJs() { + if isClass(parser.CleanValue(value)) && !strings.Contains(out, "emscripten::val::object()") { + return "reinterpret_cast(" + out + ")" + } + } else { + if strings.Contains(out, "_PackedString") { + return fmt.Sprintf("%vPacked", parser.CleanName(name, value)) + } + } + + return out +} + +//TODO: remove hex encoding once QByteArray <-> ArrayBuffer conversion is possible and/or more TypedArray functions are available for gopherjs/wasm +//TODO: make exemption for QString and QStringList for now? they usually won't need the extra hex encoding ... +//TOOD: or use malloc and simply return a pointer? instead waiting for gopherjs/wasm? +func cppOutputPackingStringForJs(name, length string) string { + if parser.UseJs() { + return fmt.Sprintf("emscripten::val ret = emscripten::val::object(); ret.set(\"data\", QByteArray::fromRawData(%v, %v).toHex().toStdString()); ret.set(\"len\", %v); ret;", name, length, length) + } + return "" +} + +func cppOutputPackingListForJs() string { + if parser.UseJs() { + return "emscripten::val ret = emscripten::val::object(); ret.set(\"data\", reinterpret_cast(tmpValue)); ret.set(\"len\", tmpValue->size());" + } + return "" +} + +func cppOutput(name, value string, f *parser.Function) string { + var vOld = value + + var tHash = sha1.New() + tHash.Write([]byte(name)) + var tHashName = hex.EncodeToString(tHash.Sum(nil)[:3]) + + name = parser.CleanName(name, value) + value = parser.CleanValue(value) + + switch value { + case "char", "qint8": + { + fSizeVariable := "-1" + for _, p := range f.Parameters { + if strings.Contains(p.Value, "int") { + fSizeVariable = parser.CleanName(p.Name, p.Value) + break + } + } + + if fSizeVariable == "-1" && strings.Contains(strings.ToLower(f.Name), "data") && parser.State.ClassMap[f.ClassName()].HasFunctionWithName("size") { + fSizeVariable = fmt.Sprintf("static_cast<%v*>(ptr)->size()", f.ClassName()) + } + + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + if parser.UseJs() { + return "({ " + cppOutputPackingStringForJs(fmt.Sprintf("const_cast(%v)", name), fSizeVariable) + " })" + } + return fmt.Sprintf("%v_PackedString { const_cast(%v), %v }", strings.Title(parser.State.ClassMap[f.ClassName()].Module), name, fSizeVariable) + } else { + if parser.UseJs() { + return "({ " + cppOutputPackingStringForJs(name, fSizeVariable) + " })" + } + return fmt.Sprintf("%v_PackedString { %v, %v }", strings.Title(parser.State.ClassMap[f.ClassName()].Module), name, fSizeVariable) + } + } + + if parser.UseJs() { + return fmt.Sprintf("({ char t%v = %v; %v })", tHashName, name, cppOutputPackingStringForJs("&t"+tHashName, "-1")) + } + return fmt.Sprintf("({ char t%v = %v; %v_PackedString { &t%v, %v }; })", tHashName, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, "-1") + } + + case "uchar", "quint8", "GLubyte": + { + fSizeVariable := "-1" + if fSizeVariable == "-1" && strings.Contains(strings.ToLower(f.Name), "bits") && len(f.Parameters) == 0 && parser.State.ClassMap[f.ClassName()].HasFunctionWithName("mappedBytes") { + fSizeVariable = fmt.Sprintf("static_cast<%v*>(ptr)->mappedBytes()", f.ClassName()) + } + + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + if parser.UseJs() { + return fmt.Sprintf("({ char* t%v = static_cast(static_cast(const_cast<%v*>(%v))); %v })", tHashName, value, name, cppOutputPackingStringForJs("t"+tHashName, fSizeVariable)) + } + return fmt.Sprintf("({ char* t%v = static_cast(static_cast(const_cast<%v*>(%v))); %v_PackedString { t%v, %v }; })", tHashName, value, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, fSizeVariable) + } + if parser.UseJs() { + return fmt.Sprintf("({ char* t%v = static_cast(static_cast(%v)); %v })", tHashName, name, cppOutputPackingStringForJs("t"+tHashName, fSizeVariable)) + } + return fmt.Sprintf("({ char* t%v = static_cast(static_cast(%v)); %v_PackedString { t%v, %v }; })", tHashName, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, fSizeVariable) + } + + if strings.Contains(vOld, "const") { + if parser.UseJs() { + return fmt.Sprintf("({ %v pret%v = %v; char* t%v = static_cast(static_cast(const_cast<%v*>(&pret%v))); %v })", vOld, tHashName, name, tHashName, value, tHashName, cppOutputPackingStringForJs("t"+tHashName, "-1")) + } + return fmt.Sprintf("({ %v pret%v = %v; char* t%v = static_cast(static_cast(const_cast<%v*>(&pret%v))); %v_PackedString { t%v, %v }; })", vOld, tHashName, name, tHashName, value, tHashName, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, "-1") + } + if parser.UseJs() { + return fmt.Sprintf("({ %v pret%v = %v; char* t%v = static_cast(static_cast(&pret%v)); %v })", vOld, tHashName, name, tHashName, tHashName, cppOutputPackingStringForJs("t"+tHashName, "-1")) + } + return fmt.Sprintf("({ %v pret%v = %v; char* t%v = static_cast(static_cast(&pret%v)); %v_PackedString { t%v, %v }; })", vOld, tHashName, name, tHashName, tHashName, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, "-1") + } + + case "QString": + { + if strings.Contains(vOld, "*") { + if parser.UseJs() { + return fmt.Sprintf("({ QByteArray t%v = %v->toUtf8(); %v })", tHashName, name, cppOutputPackingStringForJs("const_cast(t"+tHashName+".prepend(\"WHITESPACE\").constData()+10)", "t"+tHashName+".size()-10")) + } + return fmt.Sprintf("({ QByteArray t%v = %v->toUtf8(); %v_PackedString { const_cast(t%v.prepend(\"WHITESPACE\").constData()+10), t%v.size()-10 }; })", tHashName, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, tHashName) + } + if parser.UseJs() { + return fmt.Sprintf("({ QByteArray t%v = %v.toUtf8(); %v })", tHashName, name, cppOutputPackingStringForJs("const_cast(t"+tHashName+".prepend(\"WHITESPACE\").constData()+10)", "t"+tHashName+".size()-10")) + } + return fmt.Sprintf("({ QByteArray t%v = %v.toUtf8(); %v_PackedString { const_cast(t%v.prepend(\"WHITESPACE\").constData()+10), t%v.size()-10 }; })", tHashName, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, tHashName) + } + + case "QStringList": + { + if strings.Contains(vOld, "*") { + if parser.UseJs() { + return fmt.Sprintf("({ QByteArray t%v = %v->join(\"|\").toUtf8(); %v })", tHashName, name, cppOutputPackingStringForJs("const_cast(t"+tHashName+".prepend(\"WHITESPACE\").constData()+10)", "t"+tHashName+".size()-10")) + } + return fmt.Sprintf("({ QByteArray t%v = %v->join(\"|\").toUtf8(); %v_PackedString { const_cast(t%v.prepend(\"WHITESPACE\").constData()+10), t%v.size()-10 }; })", tHashName, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, tHashName) + } + if parser.UseJs() { + return fmt.Sprintf("({ QByteArray t%v = %v.join(\"|\").toUtf8(); %v })", tHashName, name, cppOutputPackingStringForJs("const_cast(t"+tHashName+".prepend(\"WHITESPACE\").constData()+10)", "t"+tHashName+".size()-10")) + } + return fmt.Sprintf("({ QByteArray t%v = %v.join(\"|\").toUtf8(); %v_PackedString { const_cast(t%v.prepend(\"WHITESPACE\").constData()+10), t%v.size()-10 }; })", tHashName, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module), tHashName, tHashName) + } + + case + "bool", "GLboolean", + + "short", "qint16", "GLshort", + "ushort", "unsigned short", "quint16", "GLushort", + + "int", "qint32", "GLint", "GLsizei", "GLintptrARB", "GLsizeiptrARB", "GLfixed", "GLclampx", + "uint", "unsigned int", "quint32", "GLenum", "GLbitfield", "GLuint", "QRgb", + + "long", + "ulong", "unsigned long", + + "longlong", "long long", "qlonglong", "qint64", + "ulonglong", "unsigned long long", "qulonglong", "quint64", + + "float", "GLfloat", "GLclampf", + "double", "qreal", + + "uintptr_t", "uintptr", "quintptr", "WId": + { + if strings.Contains(vOld, "*") { + if value == "bool" || value == "GLboolean" { + if parser.UseJs() { + if f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("reinterpret_cast(%v)", name) + } + for _, p := range append(f.Parameters, &parser.Parameter{Value: f.Output}) { + if parser.IsPackedList(p.Value) || parser.IsPackedMap(p.Value) { + return fmt.Sprintf("reinterpret_cast(%v)", name) + } + switch parser.CleanValue(p.Value) { + case "char", "qint8", "uchar", "quint8", "GLubyte", "QString", "QStringList": + return fmt.Sprintf("reinterpret_cast(%v)", name) + } + } + } + return fmt.Sprintf("reinterpret_cast(%v)", name) + } + return fmt.Sprintf("*%v", name) + } + + return name + } + + //non std types + + case "void", "GLvoid", "", "T", "JavaVM", "jclass", "jobject": + { + if value == "void" || value == "T" { + if strings.Contains(vOld, "*") && strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast(%v)", name) + } + } + + return name + } + } + + switch { + case isEnum(f.ClassName(), value): + { + if parser.UseJs() { + return fmt.Sprintf("enum_cast(%v)", name) + } + return name + } + + case isClass(value): + { + if strings.Contains(value, ".") { + value = strings.Split(value, ".")[1] + } + if strings.Contains(vOld, "*") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast<%v*>(%v)", value, name) + } + return name + } + + if strings.Contains(vOld, "&") { + if strings.Contains(vOld, "const") { + return fmt.Sprintf("const_cast<%v*>(&%v)", value, name) + } + if f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("static_cast<%v*>(&%v)", value, name) + } + } + + f.NeedsFinalizer = true + + switch value { + case "QModelIndex", "QMetaMethod", "QItemSelection": + { + return fmt.Sprintf("new %v(%v)", value, name) + } + + case "QAndroidJniObject": + { + return fmt.Sprintf("new %v(%v.object())", value, name) + } + + case "QPoint", "QPointF": + { + return fmt.Sprintf("({ %v tmpValue = %v; new %v(tmpValue.x(), tmpValue.y()); })", value, name, value) + } + + case "QSize", "QSizeF": + { + return fmt.Sprintf("({ %v tmpValue = %v; new %v(tmpValue.width(), tmpValue.height()); })", value, name, value) + } + + case "QRect", "QRectF": + { + return fmt.Sprintf("({ %v tmpValue = %v; new %v(tmpValue.x(), tmpValue.y(), tmpValue.width(), tmpValue.height()); })", value, name, value) + } + + case "QLine", "QLineF": + { + return fmt.Sprintf("({ %v tmpValue = %v; new %v(tmpValue.p1(), tmpValue.p2()); })", value, name, value) + } + + case "QMargins", "QMarginsF": + { + return fmt.Sprintf("({ %v tmpValue = %v; new %v(tmpValue.left(), tmpValue.top(), tmpValue.right(), tmpValue.bottom()); })", value, name, value) + } + } + + switch f.Fullname { + case "QColor::toVariant", "QFont::toVariant", "QImage::toVariant", "QObject::toVariant", "QIcon::toVariant", "QBrush::toVariant": + { + if f.Fullname == "QObject::toVariant" { + return fmt.Sprintf("new %v(QVariant::fromValue(%v))", value, strings.Split(name, "->")[0]) + } + return fmt.Sprintf("new %v(*%v)", value, strings.Split(name, "->")[0]) + } + + case "QVariant::toColor", "QVariant::toFont", "QVariant::toImage", "QVariant::toObject", "QVariant::toIcon", "QVariant::toBrush": + { + f.NeedsFinalizer = false + + if f.Fullname == "QVariant::toObject" { + return fmt.Sprintf("qvariant_cast<%v*>(*%v)", value, strings.Split(name, "->")[0]) + } + return fmt.Sprintf("new %v(qvariant_cast<%v>(*%v))", value, value, strings.Split(name, "->")[0]) + } + } + + for _, f := range parser.State.ClassMap[value].Functions { + if f.Meta == parser.CONSTRUCTOR { + switch len(f.Parameters) { + case 0: + { + if value == "QDataStream" { + + } else { + return fmt.Sprintf("new %v(%v)", value, name) + } + } + + case 1: + { + if parser.CleanValue(f.Parameters[0].Value) == value { + return fmt.Sprintf("new %v(%v)", value, name) + } + } + } + } + } + } + + case parser.IsPackedList(value) || parser.IsPackedMap(value): + { + if strings.HasSuffix(vOld, "*") { + if strings.Contains(vOld, "const") { + if parser.UseJs() { + return fmt.Sprintf("({ %v* tmpValue = const_cast<%v*>(%v); %v ret; })", value, value, name, cppOutputPackingListForJs()) + } + return fmt.Sprintf("({ %v* tmpValue = const_cast<%v*>(%v); %v_PackedList { tmpValue, tmpValue->size() }; })", value, value, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module)) + } + if parser.UseJs() { + return fmt.Sprintf("({ %v* tmpValue = %v; %v ret; })", value, name, cppOutputPackingListForJs()) + } + return fmt.Sprintf("({ %v* tmpValue = %v; %v_PackedList { tmpValue, tmpValue->size() }; })", value, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module)) + } + + if strings.HasSuffix(vOld, "&") { + if strings.Contains(vOld, "const") { + if parser.UseJs() { + return fmt.Sprintf("({ %v* tmpValue = const_cast<%v*>(&%v); %v ret; })", value, value, name, cppOutputPackingListForJs()) + } + return fmt.Sprintf("({ %v* tmpValue = const_cast<%v*>(&%v); %v_PackedList { tmpValue, tmpValue->size() }; })", value, value, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module)) + } + if f.SignalMode == parser.CALLBACK { + if parser.UseJs() { + return fmt.Sprintf("({ %v* tmpValue = static_cast<%v*>(&%v); %v ret; })", value, value, name, cppOutputPackingListForJs()) + } + return fmt.Sprintf("({ %v* tmpValue = static_cast<%v*>(&%v); %v_PackedList { tmpValue, tmpValue->size() }; })", value, value, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module)) + } + } + + if parser.UseJs() { + return fmt.Sprintf("({ %v* tmpValue = new %v(%v); %v ret; })", value, value, name, cppOutputPackingListForJs()) + } + return fmt.Sprintf("({ %v* tmpValue = new %v(%v); %v_PackedList { tmpValue, tmpValue->size() }; })", value, value, name, strings.Title(parser.State.ClassMap[f.ClassName()].Module)) + } + } + + f.Access = fmt.Sprintf("unsupported_cppOutput(%v)", value) + return f.Access +} + +func GoOutputJS(name, value string, f *parser.Function, p string) string { + return goOutputJS(name, value, f, p) +} + +func goOutputJS(name, value string, f *parser.Function, p string) string { + + var vOld = value + + name = parser.CleanName(name, value) + value = parser.CleanValue(value) + + switch value { + case "char", "qint8", "uchar", "quint8", "GLubyte", "QString": + { + return func() string { + var out = fmt.Sprintf("jsGoUnpackString(%v.Get(\"data\").String())", name) + if strings.Contains(p, "error") { + return fmt.Sprintf("errors.New(%v)", out) + } + return out + }() + } + + case "QStringList": + { + if f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("jsGoUnpackString(%v.Get(\"data\").String())", name) + } + return fmt.Sprintf("strings.Split(jsGoUnpackString(%v.Get(\"data\").String()), \"|\")", name) + } + + case "void", "GLvoid", "": + { + if strings.Contains(vOld, "*") { + if parser.UseWasm() { + return "unsafe.Pointer(uintptr(" + name + ".Int()))" + } + return "unsafe.Pointer(" + name + ")" + } + return name + } + + case "bool", "GLboolean": + { + if parser.UseWasm() && f.SignalMode != parser.CALLBACK { //callback arguments for wasm are proper bools, this would panic otherwise: https://github.com/golang/go/blob/master/src/syscall/js/js.go#L361 + return fmt.Sprintf("int8(%v.Int()) != 0", name) + } + return fmt.Sprintf("%v.Bool()", name) + } + + case "short", "qint16", "GLshort": + { + if parser.UseWasm() { + return fmt.Sprintf("int16(%v.Int())", name) + } + return fmt.Sprintf("int16(%v.Int64())", name) + } + + case "ushort", "unsigned short", "quint16", "GLushort": + { + if parser.UseWasm() { + return fmt.Sprintf("uint16(%v.Int())", name) + } + return fmt.Sprintf("uint16(%v.Uint64())", name) + } + + case "int", "qint32", "GLint", "GLsizei", "GLintptrARB", "GLsizeiptrARB", "GLfixed", "GLclampx": + { + if parser.UseWasm() { + return fmt.Sprintf("int(int32(%v.Int()))", name) + } + return fmt.Sprintf("int(int32(%v.Int64()))", name) + } + + case "uint", "unsigned int", "quint32", "GLenum", "GLbitfield", "GLuint", "QRgb": + { + if parser.UseWasm() { + return fmt.Sprintf("uint(uint32(%v.Int()))", name) + } + return fmt.Sprintf("uint(uint32(%v.Uint64()))", name) + } + + case "long": + { + if parser.UseWasm() { + return fmt.Sprintf("int(int32(%v.Int()))", name) + } + return fmt.Sprintf("int(int32(%v.Int64()))", name) + } + + case "ulong", "unsigned long": + { + if parser.UseWasm() { + return fmt.Sprintf("uint(uint32(%v.Int()))", name) + } + return fmt.Sprintf("uint(uint32(%v.Uint64()))", name) + } + + case "longlong", "long long", "qlonglong", "qint64": + { + if parser.UseWasm() { + return fmt.Sprintf("int64(%v.Int())", name) + } + return fmt.Sprintf("int64(%v.Int64())", name) + } + + case "ulonglong", "unsigned long long", "qulonglong", "quint64": + { + if parser.UseWasm() { + return fmt.Sprintf("uint64(%v.Int())", name) + } + return fmt.Sprintf("uint64(%v.Uint64())", name) + } + + case "float", "GLfloat", "GLclampf": + { + return fmt.Sprintf("float32(%v.Float())", name) + } + + case "double", "qreal": + { + return fmt.Sprintf("float64(%v.Float())", name) + } + + case "uintptr_t", "uintptr", "quintptr", "WId": + { + if parser.UseJs() { + if parser.UseWasm() { + return fmt.Sprintf("uintptr(%v.Int())", name) + } + return fmt.Sprintf("uintptr(%v.Unsafe())", name) + } + return fmt.Sprintf("uintptr(%v)", name) + } + + //non std types + + case "T", "JavaVM", "jclass", "jobject": + { + switch f.TemplateModeJNI { + case "Boolean": + { + if parser.UseWasm() { + return fmt.Sprintf("int8(%v.Int()) != 0", name) + } + return fmt.Sprintf("%v.Bool()", name) + } + + case "Int": + { + return fmt.Sprintf("int(int32(%v.Uint64()))", name) + } + + case "Void": + { + return name + } + } + + return fmt.Sprintf("unsafe.Pointer(%v)", name) + } + } + + switch { + case isEnum(f.ClassName(), value): + { + if c, ok := parser.State.ClassMap[class(cppEnum(f, value, false))]; ok && module(c.Module) != module(f) && module(c.Module) != "" { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[c.Module]; ok { + if parser.UseWasm() { + return fmt.Sprintf("int64(%v.Int())", name) + } + return fmt.Sprintf("int64(%v.Int64())", name) + } + if parser.UseWasm() { + return fmt.Sprintf("%v.%v(%v.Int())", module(c.Module), goEnum(f, value), name) + } + return fmt.Sprintf("%v.%v(%v.Int64())", module(c.Module), goEnum(f, value), name) + } + if parser.UseWasm() { + return fmt.Sprintf("%v(%v.Int())", goEnum(f, value), name) + } + return fmt.Sprintf("%v(%v.Int64())", goEnum(f, value), name) + } + + case isClass(value): + { + if parser.UseWasm() && f.SignalMode == parser.CALLBACK { + return fmt.Sprintf("uintptr(%v.Int())", name) + } + + if strings.Contains(value, ".") { + value = strings.Split(value, ".")[1] + } + if m := module(parser.State.ClassMap[value].Module); m != module(f) { + if _, ok := parser.State.ClassMap[f.ClassName()].WeakLink[parser.State.ClassMap[value].Module]; ok { + if parser.UseWasm() { + return fmt.Sprintf("unsafe.Pointer(uintptr(%v.Int()))", name) + } + return fmt.Sprintf("unsafe.Pointer(%v.Unsafe())", name) + } + if parser.UseWasm() { + return fmt.Sprintf("%v.New%vFromPointer(unsafe.Pointer(uintptr(%v.Int())))", m, strings.Title(value), name) + } + return fmt.Sprintf("%v.New%vFromPointer(unsafe.Pointer(%v.Unsafe()))", m, strings.Title(value), name) + } + if parser.UseWasm() { + return fmt.Sprintf("New%vFromPointer(unsafe.Pointer(uintptr(%v.Int())))", strings.Title(value), name) + } + return fmt.Sprintf("New%vFromPointer(unsafe.Pointer(%v.Unsafe()))", strings.Title(value), name) + } + + case parser.IsPackedList(value): + { + if parser.UseWasm() { + return fmt.Sprintf("func(l js.Value)%v{out := make(%v, int(l.Get(\"len\").Int()))\ntmpList := New%vFromPointer(unsafe.Pointer(uintptr(l.Get(\"data\").Int())))\nfor i:=0;i key { + switch deduced := input[key].(type) { + case string: + jObject := QAndroidJniObject_FromString(deduced) + + return jObject.Object(), func() { jObject.DestroyQAndroidJniObject() } + + case []string: + jObject := QAndroidJniObject_FromString(strings.Join(deduced, ",,,")) + jObject2 := jObject.CallObjectMethod2("split", "(Ljava/lang/String;)[Ljava/lang/String;", ",,,") + jObject.DestroyQAndroidJniObject() + + return jObject2.Object(), func() { jObject2.DestroyQAndroidJniObject() } + + case bool: + return unsafe.Pointer(uintptr(C.char(int8(qt.GoBoolToInt(deduced))))), nil + + case int16: + return unsafe.Pointer(uintptr(C.short(deduced))), nil + + case uint16: + return unsafe.Pointer(uintptr(C.ushort(deduced))), nil + + case int: + return unsafe.Pointer(uintptr(C.int(int32(deduced)))), nil + + case uint: + return unsafe.Pointer(uintptr(C.uint(uint32(deduced)))), nil + + case int32: + return unsafe.Pointer(uintptr(C.int(deduced))), nil + + case uint32: + return unsafe.Pointer(uintptr(C.uint(deduced))), nil + + case int64: + return unsafe.Pointer(uintptr(C.longlong(deduced))), nil + + case uint64: + return unsafe.Pointer(uintptr(C.ulonglong(deduced))), nil + + case float32: + return unsafe.Pointer(uintptr(C.float(deduced))), nil + + case float64: + return unsafe.Pointer(uintptr(C.double(deduced))), nil + + case uintptr: + return unsafe.Pointer(deduced), nil + + case unsafe.Pointer: + return deduced, nil + + case *QAndroidJniObject: + return deduced.Object(), nil + } + } + return nil, nil +} diff --git a/qt/tool-chain/binding/parser/class.go b/qt/tool-chain/binding/parser/class.go new file mode 100644 index 0000000..89f36bc --- /dev/null +++ b/qt/tool-chain/binding/parser/class.go @@ -0,0 +1,365 @@ +package parser + +import ( + "crypto/sha1" + "encoding/hex" + "fmt" + "os" + "strings" + "sync" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +type Class struct { + Name string `xml:"name,attr"` + Status string `xml:"status,attr"` + Access string `xml:"access,attr"` + Abstract bool `xml:"abstract,attr"` + Bases string `xml:"bases,attr"` + Module string `xml:"module,attr"` + Brief string `xml:"brief,attr"` + Functions []*Function `xml:"function"` + Enums []*Enum `xml:"enum"` + Variables []*Variable `xml:"variable"` + Properties []*Variable `xml:"property"` + Classes []*Class `xml:"class"` + Since string `xml:"since,attr"` + + DocModule string + Stub bool + WeakLink map[string]struct{} + Export bool + Fullname string + Pkg string + Path string + HasFinalizer bool + + Constructors []string + Derivations []string + + sync.Mutex +} + +func (c *Class) register(m *Module) { + c.DocModule = c.Module + c.Module = m.Project + c.Pkg = m.Pkg + State.ClassMap[c.Name] = c + + for _, sc := range c.Classes { + if sc.Name != "PaintContext" { //TODO: remove and support all sub classes + continue + } + + sc.Fullname = fmt.Sprintf("%v::%v", c.Name, sc.Name) + sc.register(m) + } +} + +func (c *Class) derivation() { + for _, b := range c.GetBases() { + if bc, e := State.ClassMap[b]; e { + bc.Derivations = append(bc.Derivations, c.Name) + } + } +} + +func (c *Class) GetBases() []string { + if c == nil || c.Bases == "" { + return make([]string, 0) + } + if strings.Contains(c.Bases, ",") { + return strings.Split(c.Bases, ",") + } + return []string{c.Bases} +} + +func (c *Class) GetAllBases() []string { + var out, _ = c.GetAllBasesRecursiveCheckFailed(0) + return out +} + +func (c *Class) GetAllBasesRecursiveCheckFailed(i int) ([]string, bool) { + var input = make([]string, 0) + + i++ + if i > 100 { + return input, true + } + + for _, b := range c.GetBases() { + var bc, ok = State.ClassMap[b] + if !ok { + continue + } + + input = append(input, b) + var bs, isRecursive = bc.GetAllBasesRecursiveCheckFailed(i) + if isRecursive { + return input, true + } + for _, sbc := range bs { + input = append(input, sbc) + } + } + + return input, false +} + +func (c *Class) IsSubClassOfQObject() bool { + return c.IsSubClassOf("QObject") +} + +func (c *Class) IsSubClassOf(class string) bool { + if c == nil { + return false + } + + for _, bcn := range append([]string{c.Name}, c.GetAllBases()...) { + if bcn == class { + return true + } + } + + return false +} + +func (c *Class) isSubClass() bool { return c.Fullname != "" } + +func (c *Class) GetAllDerivations() []string { + + var input = make([]string, 0) + + for _, b := range c.Derivations { + var bc, exists = State.ClassMap[b] + if !exists { + continue + } + + input = append(input, b) + for _, sbc := range bc.GetAllDerivations() { + input = append(input, sbc) + } + } + + return input +} + +func (c *Class) GetAllDerivationsInSameModule() []string { + + var input = make([]string, 0) + + for _, i := range c.GetAllDerivations() { + if State.ClassMap[i].Module == c.Module && i != "QWinEventNotifier" && i != "QFutureWatcher" { + input = append(input, i) + } + } + + return input +} + +func (c *Class) HasFunction(f *Function) bool { + for _, cf := range c.Functions { + if cf.Name == f.Name && cf.Virtual == f.Virtual && + cf.Meta == f.Meta && + cf.Output == f.Output && len(cf.Parameters) == len(f.Parameters) { + + var similar = true + for i, cfp := range cf.Parameters { + if cfp.Value != f.Parameters[i].Value { + similar = false + } + } + if similar { + return true + } + } + } + + return false +} + +func (c *Class) HasFunctionWithName(n string) bool { + return c.HasFunctionWithNameAndOverloadNumber(n, "") +} + +func (c *Class) HasFunctionWithNameAndOverloadNumber(n string, num string) bool { + for _, f := range c.Functions { + if strings.ToLower(f.Name) == strings.ToLower(n) && f.OverloadNumber == num { + return true + } + } + return false +} + +func (c *Class) IsPolymorphic() bool { return len(c.GetBases()) > 1 } + +func (c *Class) HasConstructor() bool { + for _, f := range c.Functions { + if f.Meta == CONSTRUCTOR || f.Meta == COPY_CONSTRUCTOR || f.Meta == MOVE_CONSTRUCTOR { + return true + } + } + return false +} + +func (c *Class) HasDestructor() bool { + for _, f := range c.Functions { + if f.Meta == DESTRUCTOR { + return true + } + } + return false +} + +func (c *Class) HasCallbackFunctions() bool { + for _, bcn := range append([]string{c.Name}, c.GetAllBases()...) { + var bc, ok = State.ClassMap[bcn] + if !ok { + continue + } + for _, f := range bc.Functions { + if f.Virtual == IMPURE || f.Virtual == PURE || f.Meta == SIGNAL || f.Meta == SLOT { + return true + } + } + } + return false +} + +func (c *Class) IsSupported() bool { + if c == nil { + return false + } + + switch c.Name { + case "QCborStreamReader", "QCborStreamWriter", "QCborValue", "QScopeGuard", "QTest", + "QImageReaderWriterHelpers", "QPasswordDigestor", "QDtls", "QDtlsClientVerifier": + c.Access = "unsupported_isBlockedClass" + return false + } + + if utils.QT_VERSION_NUM() >= 5080 { + switch c.Name { + case "QSctpServer", "QSctpSocket", "Http2", "QAbstractExtensionFactory": + { + c.Access = "unsupported_isBlockedClass" + return false + } + } + } + + if UseJs() { + if strings.HasPrefix(c.Name, "QOpenGLFunctions_") || strings.HasPrefix(c.Name, "QSsl") || strings.HasPrefix(c.Name, "QNetwork") { + c.Access = "unsupported_isBlockedClass" + return false + } + switch c.Name { + case "QThreadPool", "QSharedMemory", "QPluginLoader", "QSemaphore", "QSemaphoreReleaser", + "QSystemSemaphore", "QThread", "QWaitCondition", "QUnhandledException", "QFileSystemModel", + "QLibrary", "QUdpSocket", "QHttpMultiPart", "QHttpPart", "QOpenGLTimeMonitor", "QOpenGLTimerQuery", + "QRemoteObjectHost": //TODO: only block in 5.11 ? + { + c.Access = "unsupported_isBlockedClass" + return false + } + } + } + + switch c.Name { + case + "QString", "QStringList", //mapped to primitive + + "QExplicitlySharedDataPointer", "QFuture", "QDBusPendingReply", "QDBusReply", "QFutureSynchronizer", //needs template + "QGlobalStatic", "QMultiHash", "QQueue", "QMultiMap", "QScopedPointer", "QSharedDataPointer", + "QScopedArrayPointer", "QSharedPointer", "QThreadStorage", "QScopedValueRollback", "QVarLengthArray", + "QWeakPointer", "QWinEventNotifier", + + "QFlags", "QException", "QStandardItemEditorCreator", "QSGSimpleMaterialShader", "QGeoCodeReply", "QFutureWatcher", //other + "QItemEditorCreator", "QGeoCodingManager", "QGeoCodingManagerEngine", "QQmlListProperty", + + "QPlatformGraphicsBuffer", "QPlatformSystemTrayIcon", "QRasterPaintEngine", "QSupportedWritingSystems", "QGeoLocation", //file not found or QPA API + "QAbstractOpenGLFunctions", + + "QProcess", "QProcessEnvironment", //TODO: iOS + + "QRemoteObjectPackets", + + "QStaticByteArrayMatcher", "QtDummyFutex", "QtLinuxFutex", + "QShaderLanguage", + + "AndroidNfc", "OSXBluetooth", + + "QtROClientFactory", "QtROServerFactory", + + "QWebViewFactory", "QGeoServiceProviderFactoryV2", + + "QtDwmApiDll", "QWinMime", + + "QAbstract3DGraph": //TODO: only for arch with pkg_config + { + c.Access = "unsupported_isBlockedClass" + return false + } + } + + switch { + case + c.Name == "QOpenGLFunctions_ES2", strings.HasPrefix(c.Name, "QPlace"), //file not found or QPA API + + strings.HasPrefix(c.Name, "QAtomic"), //other + + strings.HasSuffix(c.Name, "terator"), strings.Contains(c.Brief, "emplate"), //needs template + + strings.HasPrefix(c.Name, "QVulkan"): + + { + c.Access = "unsupported_isBlockedClass" + return false + } + } + + if strings.HasPrefix(c.Name, "QOpenGL") && (os.Getenv("DEB_TARGET_ARCH_CPU") == "arm" || (strings.HasPrefix(State.Target, "android") && utils.QT_FAT())) { //TODO: block indiv classes for fat android build instead + c.Access = "unsupported_isBlockedClass" + return false + } + + if utils.QT_VERSION_NUM() <= 5042 { + if c.Name == "QQmlAbstractProfilerAdapter" || + c.Name == "QGraphicsLayout" || + c.Name == "QQmlAbstractProfilerAdapter" || + c.Name == "QDeclarativeMultimedia" { + c.Access = "unsupported_isBlockedClass" + return false + } + } + + if strings.HasPrefix(c.Name, "QOpenGLFunctions_") && !utils.QT_GEN_OPENGL() { + c.Access = "unsupported_isBlockedClass" + return false + } + + if State.Minimal { + return c.Export + } + + return true +} + +func (c *Class) GetFunction(fname string) *Function { + for _, f := range c.Functions { + if f.Name == fname { + return f + } + } + return nil +} + +// + +func (c *Class) Hash() string { + h := sha1.New() + h.Write([]byte(c.Path)) + return hex.EncodeToString(h.Sum(nil)[:3]) +} diff --git a/qt/tool-chain/binding/parser/class_add.go b/qt/tool-chain/binding/parser/class_add.go new file mode 100644 index 0000000..dd90e5b --- /dev/null +++ b/qt/tool-chain/binding/parser/class_add.go @@ -0,0 +1,259 @@ +package parser + +import ( + "fmt" + "strings" +) + +func (c *Class) add() { + c.addGeneralFuncs() + + c.addVarAndPropFuncs() + + c.addMocFuncs() +} + +func (c *Class) addGeneralFuncs() { + switch c.Name { + case "QColor", "QFont", "QImage", "QObject", "QIcon", "QBrush": + { + c.Functions = append(c.Functions, &Function{ + Name: "toVariant", + Fullname: fmt.Sprintf("%v::toVariant", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "QVariant", + Parameters: []*Parameter{}, + Signature: "()", + }) + } + + case "QVariant": + { + for _, name := range []string{"toColor", "toFont", "toImage", "toObject", "toIcon", "toBrush"} { + c.Functions = append(c.Functions, &Function{ + Name: name, + Fullname: fmt.Sprintf("%v::%v", c.Name, name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: strings.Replace(name, "to", "Q", -1), + Parameters: []*Parameter{}, + Signature: "()", + }) + } + } + + case "QQmlEngine": + { + //http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType-2 + //int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) + c.Functions = append(c.Functions, &Function{ + Name: "qmlRegisterSingletonType", + Fullname: fmt.Sprintf("%v::qmlRegisterSingletonType", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + NonMember: true, + Static: true, + Output: fmt.Sprintf("int"), + Parameters: []*Parameter{ + {Name: "url", Value: "const QUrl &"}, + {Name: "uri", Value: "const char *"}, + {Name: "versionMajor", Value: "int"}, + {Name: "versionMinor", Value: "int"}, + {Name: "qmlName", Value: "const char *"}, + }, + Signature: "(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)", + }) + + //http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterType-2 + //int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) + c.Functions = append(c.Functions, &Function{ + Name: "qmlRegisterType", + Fullname: fmt.Sprintf("%v::qmlRegisterType", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + NonMember: true, + Static: true, + Output: fmt.Sprintf("int"), + Parameters: []*Parameter{ + {Name: "url", Value: "const QUrl &"}, + {Name: "uri", Value: "const char *"}, + {Name: "versionMajor", Value: "int"}, + {Name: "versionMinor", Value: "int"}, + {Name: "qmlName", Value: "const char *"}, + }, + Signature: "(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)", + }) + } + + case "QAndroidJniEnvironment": + { + c.Functions = append(c.Functions, &Function{ + Name: "ExceptionCheck", + Fullname: fmt.Sprintf("%v::ExceptionCheck", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "bool", + Parameters: []*Parameter{}, + Signature: "()", + }) + + c.Functions = append(c.Functions, &Function{ + Name: "ExceptionDescribe", + Fullname: fmt.Sprintf("%v::ExceptionDescribe", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "void", + Parameters: []*Parameter{}, + Signature: "()", + }) + + c.Functions = append(c.Functions, &Function{ + Name: "ExceptionClear", + Fullname: fmt.Sprintf("%v::ExceptionClear", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "void", + Parameters: []*Parameter{}, + Signature: "()", + }) + + c.Functions = append(c.Functions, &Function{ + Name: "ExceptionOccurred", + Fullname: fmt.Sprintf("%v::ExceptionOccurred", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "void*", + Parameters: []*Parameter{}, + Signature: "()", + }) + } + + case "QVideoFrame": + { + //QImage qt_imageFromVideoFrame(const QVideoFrame &frame) + /* requires multimedia-private + c.Functions = append(c.Functions, &Function{ + Name: "qt_imageFromVideoFrame", + Fullname: fmt.Sprintf("%v::qt_imageFromVideoFrame", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + NonMember: true, + Static: true, + Output: fmt.Sprintf("QImage"), + Parameters: []*Parameter{ + {Name: "frame", Value: "const QVideoFrame &"}, + }, + Signature: "(const QVideoFrame &frame)", + }) + */ + } + } + + //TODO: make general + if c.Name == "QQmlNetworkAccessManagerFactory" && !c.HasConstructor() { + c.Functions = append(c.Functions, &Function{ + Name: c.Name, + Fullname: fmt.Sprintf("%v::%v", c.Name, c.Name), + Access: "public", + Virtual: "non", + Meta: CONSTRUCTOR, + Parameters: []*Parameter{}, + Signature: "()", + }) + } +} + +func (c *Class) addVarAndPropFuncs() { + for _, v := range c.Variables { + c.Functions = append(c.Functions, v.varToFunc()...) + } + for _, p := range c.Properties { + c.Functions = append(c.Functions, p.propToFunc(c)...) + } +} + +func (c *Class) addMocFuncs() { + if c.Module != MOC { + return + } + + if c.HasFunctionWithName("qRegisterMetaType") { + return + } + + //http://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1 + //int qRegisterMetaType() + qRF := &Function{ + Name: "qRegisterMetaType", + Fullname: fmt.Sprintf("%v::qRegisterMetaType", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + NonMember: true, + NoMocDeduce: true, + Static: true, + Output: fmt.Sprintf("int"), + Parameters: []*Parameter{}, + Signature: "()", + TemplateModeGo: fmt.Sprintf("%v*", c.Name), + } + c.Functions = append(c.Functions, qRF) + + //http://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType + //int qRegisterMetaType(const char *typeName) + qRF2 := *qRF + qRF2.Overload = true + qRF2.OverloadNumber = "2" + qRF2.Parameters = []*Parameter{{Name: "typeName", Value: "const char *"}} + qRF2.Signature = "(const char *typeName)" + c.Functions = append(c.Functions, &qRF2) + + if c.IsSubClassOf("QCoreApplication") { + return + } + + //http://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterType + //int qmlRegisterType() + qmlF := &Function{ + Name: "qmlRegisterType", + Fullname: fmt.Sprintf("%v::qmlRegisterType", c.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + NonMember: true, + NoMocDeduce: true, + Static: true, + Output: fmt.Sprintf("int"), + Parameters: []*Parameter{}, + Signature: "()", + TemplateModeGo: fmt.Sprintf("%v", c.Name), + } + c.Functions = append(c.Functions, qmlF) + + //int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName) + qmlF2 := *qmlF + qmlF2.Overload = true + qmlF2.OverloadNumber = "2" + qmlF2.Parameters = []*Parameter{ + {Name: "uri", Value: "const char *"}, + {Name: "versionMajor", Value: "int"}, + {Name: "versionMinor", Value: "int"}, + {Name: "qmlName", Value: "const char *"}, + } + qmlF2.Signature = "(const char *uri, int versionMajor, int versionMinor, const char *qmlName)" + c.Functions = append(c.Functions, &qmlF2) +} diff --git a/qt/tool-chain/binding/parser/class_fix.go b/qt/tool-chain/binding/parser/class_fix.go new file mode 100644 index 0000000..18deb00 --- /dev/null +++ b/qt/tool-chain/binding/parser/class_fix.go @@ -0,0 +1,475 @@ +package parser + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func (c *Class) fix() { + c.fixFunctions((*Function).fix) + + c.fixEnums() + + //c.fixGeneral() + c.fixGeneral_Version() + + c.fixLinkage() + + c.fixBases() + + c.FixGenericHelper() + + c.fixFunctions((*Function).fixGeneral_AfterClasses) +} + +func (c *Class) fixFunctions(fix func(*Function)) { + for _, f := range c.Functions { + fix(f) + } +} + +//TODO: (*Enum) IsSupported +//TODO: merge into (*Class).removeEnums_Version +func (c *Class) fixEnums() { + for _, e := range c.Enums { + if e.Fullname == "QVariant::Type" { + e.Status = "active" + for i := len(e.Values) - 1; i >= 0; i-- { + if v := e.Values[i]; v.Name == "LastCoreType" || v.Name == "LastGuiType" { + e.Values = append(e.Values[:i], e.Values[i+1:]...) + } + } + } + if e.Fullname == "QFileSystemModel::Roles" && utils.QT_VERSION_NUM() <= 5042 { + for i := len(e.Values) - 1; i >= 0; i-- { + if v := e.Values[i]; v.Name == "FileIconRole" { + e.Values = append(e.Values[:i], e.Values[i+1:]...) + } + } + } + if utils.QT_MACPORTS() { + if e.Fullname == "QWebSettings::WebAttribute" { + for i := len(e.Values) - 1; i >= 0; i-- { + if v := e.Values[i]; v.Name == "MediaSourceEnabled" || v.Name == "MediaEnabled" || v.Name == "WebSecurityEnabled" || v.Name == "FullScreenSupportEnabled" { + e.Values = append(e.Values[:i], e.Values[i+1:]...) + } + } + } + if e.Fullname == "QWebPage::MessageSource" { + for i := len(e.Values) - 1; i >= 0; i-- { + if v := e.Values[i]; v.Name == "MessageSource" || v.Name == "MessageLevel" { + e.Values = append(e.Values[:i], e.Values[i+1:]...) + } + } + } + } + } +} + +func (c *Class) fixGeneral_Version() { + switch c.Name { + case "QStyle": + { + for _, f := range c.Functions { + if f.Name != "standardIcon" { + continue + } + + var tmpF = *f + tmpF.Name = "standardPixmap" + tmpF.Output = "QPixmap" + tmpF.Fullname = fmt.Sprintf("%v::%v", c.Name, tmpF.Name) + + c.Functions = append(c.Functions, &tmpF) + } + } + + case "QScxmlCppDataModel": + { + for _, s := range []struct{ Name, Output string }{ + {"evaluateToString", "QString"}, + {"evaluateToBool", "bool"}, + {"evaluateToVariant", "QVariant"}, + {"evaluateToVoid", "void"}, + {"evaluateAssignment", "void"}, + {"evaluateInitialization", "void"}, + } { + c.Functions = append(c.Functions, &Function{ + Name: s.Name, + Fullname: fmt.Sprintf("%v::%v", c.Name, s.Name), + Access: "public", + Virtual: PURE, + Meta: PLAIN, + Output: s.Output, + Parameters: []*Parameter{ + { + Name: "id", + Value: "QScxmlExecutableContent::EvaluatorId", + }, + { + Name: "ok", + Value: "bool*", + }, + }, + Signature: "()", + }) + } + + c.Functions = append(c.Functions, &Function{ + Name: "evaluateForeach", + Fullname: fmt.Sprintf("%v::evaluateForeach", c.Name), + Access: "public", + Virtual: PURE, + Meta: PLAIN, + Output: "void", + Parameters: []*Parameter{ + { + Name: "id", + Value: "QScxmlExecutableContent::EvaluatorId", + }, + { + Name: "ok", + Value: "bool*", + }, + { + Name: "body", + Value: "ForeachLoopBody*", + }, + }, + Signature: "()", + }) + } + + case "QOperatingSystemVersion": + { + if utils.QT_VERSION_NUM() >= 5093 { + c.Functions = append(c.Functions, &Function{ + Name: "QOperatingSystemVersion", + Fullname: fmt.Sprintf("%v::QOperatingSystemVersion", c.Name), + Access: "public", + Virtual: "non", + Meta: CONSTRUCTOR, + Status: "active", + Parameters: []*Parameter{ + { + Name: "other", + Value: "const QOperatingSystemVersion &", + }, + }, + Signature: "QOperatingSystemVersion(const QOperatingSystemVersion &other) = default", + }) + } + } + case "QDesktopWidget": + { + for _, f := range c.Functions { + f.Status = "active" + } + } + } +} + +func (c *Class) fixLinkage() { + switch c.Module { + case "QtCore": + { + c.WeakLink = map[string]struct{}{ + "QtGui": struct{}{}, + "QtWidgets": struct{}{}, + } + } + + case "QtGui": + { + c.WeakLink = map[string]struct{}{ + "QtWidgets": struct{}{}, + "QtMultimedia": struct{}{}, + } + } + } +} + +var pkgConfigIncludeDir string + +func (c *Class) fixBases() { + if c.Module == MOC || c.Pkg != "" { + return + } + + if c.Bases == "QList" { + c.Bases = "" + return + } + + switch c.Name { + case "QChart", "QLegend": + c.Bases = "QGraphicsWidget" + + case "QChartView": + c.Bases = "QGraphicsView" + + case "QCandlestickModelMapper", "QHBarModelMapper", "QHBoxPlotModelMapper", + "QVBarModelMapper", "QVBoxPlotModelMapper", "QTextToSpeech": + c.Bases = "QObject" + } + + if c.Module == "QtCharts" { + return + } + + //if utils.QT_VERSION() == "5.8.0" { + if c.Name == "QDesignerCustomWidgetInterface" || + c.Name == "QDesignerCustomWidgetCollectionInterface" { + return + } + //} + + var ( + prefixPath string + infixPath = "include" + suffixPath = string(filepath.Separator) + ) + + switch runtime.GOOS { + case "windows": + { + if utils.QT_MSYS2() { + prefixPath = utils.QT_MSYS2_DIR() + if utils.QT_MSYS2_STATIC() { + prefixPath = filepath.Join(prefixPath, "qt5-static") + } + } else { + prefixPath = filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "mingw73_64") + if !utils.ExistsDir(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR())) { + prefixPath = filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "mingw73_64") + } + if !utils.ExistsDir(prefixPath) { + prefixPath = strings.Replace(prefixPath, "mingw73_64", "mingw53_32", -1) + } + if !utils.ExistsDir(prefixPath) { + prefixPath = strings.Replace(prefixPath, "mingw53_32", "mingw49_32", -1) + } + } + } + + case "darwin": + { + if utils.QT_NIX() { + infixPath = "include" + suffixPath = string(filepath.Separator) + for _, qmakepath := range strings.Split(os.Getenv("QMAKEPATH"), string(filepath.ListSeparator)) { + if utils.ExistsFile(filepath.Join(qmakepath, infixPath, c.DocModule+suffixPath+c.Name)) { + prefixPath = qmakepath + break + } + } + } else { + prefixPath = utils.QT_DARWIN_DIR() + infixPath = "lib" + suffixPath = ".framework/Headers/" + } + } + + case "linux": + { + switch { + case utils.QT_PKG_CONFIG(): + if pkgConfigIncludeDir == "" { + pkgConfigIncludeDir = strings.TrimSpace(utils.RunCmd(exec.Command("pkg-config", "--variable=includedir", "Qt5Core"), "parser.class_includedir")) + } + prefixPath = pkgConfigIncludeDir + case utils.QT_SAILFISH(): + prefixPath = "/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-i486/usr/include/qt5" + infixPath = "" + default: + prefixPath = filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "gcc_64") + if !utils.ExistsDir(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR())) { + prefixPath = filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "gcc_64") + } + } + } + } + + //TODO: remove + switch c.Name { + case "Qt", "QtGlobalStatic", "QUnicodeTools", "QHooks", "QModulesPrivate", "QtMetaTypePrivate", "QUnicodeTables", "QAndroidJniEnvironment", "QAndroidJniObject", "QAndroidActivityResultReceiver", "QSupportedWritingSystems", "QAbstractOpenGLFunctions": + { + c.Bases = "" + return + } + + case "QFutureWatcher", "QDBusAbstractInterface": + { + c.Bases = "QObject" + return + } + + case "QDBusPendingReply": + { + c.Bases = "QDBusPendingCall" + return + } + + case "QRasterPaintEngine": + { + c.Bases = "QPaintEngine" + return + } + + case "QUiLoader", "QEGLNativeContext", "QWGLNativeContext", "QGLXNativeContext", "QEglFSFunctions", "QWindowsWindowFunctions", "QCocoaNativeContext", "QXcbWindowFunctions", "QCocoaWindowFunctions": + { + if utils.QT_PKG_CONFIG() { + c.Bases = getBasesFromHeader(utils.LoadOptional(filepath.Join(prefixPath, c.Module, strings.ToLower(c.Name)+".h")), c.Name, c.Module) + } else { + c.Bases = getBasesFromHeader(utils.Load(filepath.Join(prefixPath, "include", c.Module, strings.ToLower(c.Name)+".h")), c.Name, c.Module) + } + return + } + + case "QPlatformSystemTrayIcon", "QPlatformGraphicsBuffer": + { + if utils.QT_PKG_CONFIG() { + c.Bases = getBasesFromHeader(utils.LoadOptional(filepath.Join(prefixPath, c.Module, utils.QT_VERSION(), c.Module, "qpa", strings.ToLower(c.Name)+".h")), c.Name, c.Module) + } else { + c.Bases = getBasesFromHeader(utils.Load(filepath.Join(prefixPath, infixPath, c.Module+suffixPath+utils.QT_VERSION(), "QtGui", "qpa", strings.ToLower(c.Name)+".h")), c.Name, c.Module) + } + return + } + + case "QColumnView", "QLCDNumber", "QWebEngineUrlSchemeHandler", "QWebEngineUrlRequestInterceptor", "QWebEngineCookieStore", "QWebEngineUrlRequestInfo", "QWebEngineUrlRequestJob": + { + for _, m := range append(LibDeps[strings.TrimPrefix(c.Module, "Qt")], strings.TrimPrefix(c.Module, "Qt")) { + m = fmt.Sprintf("Qt%v", m) + if utils.QT_PKG_CONFIG() { + if utils.ExistsFile(filepath.Join(prefixPath, m, strings.ToLower(c.Name)+".h")) { + c.Bases = getBasesFromHeader(utils.LoadOptional(filepath.Join(prefixPath, m, strings.ToLower(c.Name)+".h")), c.Name, c.Module) + return + } + } else { + if utils.ExistsFile(filepath.Join(prefixPath, infixPath, m+suffixPath+c.Name)) { + c.Bases = getBasesFromHeader(utils.Load(filepath.Join(prefixPath, infixPath, m+suffixPath+strings.ToLower(c.Name)+".h")), c.Name, c.Module) + return + } + } + } + } + } + + //TODO: + var libs = append(LibDeps[strings.TrimPrefix(c.Module, "Qt")], strings.TrimPrefix(c.Module, "Qt")) + for i, v := range libs { + if v == "TestLib" { + libs[i] = "Test" + } + } + + var found bool + for _, m := range libs { + m = fmt.Sprintf("Qt%v", m) + if utils.QT_PKG_CONFIG() { + if utils.ExistsFile(filepath.Join(prefixPath, m, c.Name)) { + + var f = utils.LoadOptional(filepath.Join(prefixPath, m, c.Name)) + if f != "" { + found = true + c.Bases = getBasesFromHeader(utils.LoadOptional(filepath.Join(prefixPath, m, strings.Split(f, "\"")[1])), c.Name, m) + } + break + } + } else { + if utils.ExistsFile(filepath.Join(prefixPath, infixPath, m+suffixPath+c.Name)) { + var f = utils.Load(filepath.Join(prefixPath, infixPath, m+suffixPath+c.Name)) + if f != "" { + found = true + c.Bases = getBasesFromHeader(utils.Load(filepath.Join(filepath.Join(prefixPath, infixPath, m+suffixPath), strings.Split(f, "\"")[1])), c.Name, m) + } + break + } + } + } + + if !found && c.Name != "SailfishApp" && c.Fullname == "" { + utils.Log.WithField("module", strings.TrimPrefix(c.Module, "Qt")).WithField("class", c.Name).Debugln("failed to find header file") + } +} + +func getBasesFromHeader(f string, n string, m string) string { + + f = strings.Replace(f, "\r", "", -1) + + if strings.HasSuffix(n, "Iterator") { + return "" + } + + for i, l := range strings.Split(f, "\n") { + + //TODO: reduce + if strings.Contains(l, "class "+n) || + strings.Contains(l, "class Q_"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"_EXPORT "+n) || strings.Contains(l, "class Q"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"_EXPORT "+n) || + strings.Contains(l, "class Q_"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"CORE_EXPORT "+n) || strings.Contains(l, "class Q"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"CORE_EXPORT "+n) || + strings.Contains(l, "class QDESIGNER_SDK_EXPORT "+n) || strings.Contains(l, "class QDESIGNER_EXTENSION_EXPORT "+n) || strings.Contains(l, "class QDESIGNER_UILIB_EXPORT "+n) || strings.Contains(l, "class "+n) || strings.Contains(l, "class Q_"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"_EXPORT "+n) || strings.Contains(l, "class Q"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"_EXPORT "+n) || strings.Contains(l, "class QDESIGNER_SDK_EXPORT "+n) || strings.Contains(l, "class QDESIGNER_EXTENSION_EXPORT "+n) || strings.Contains(l, "class QDESIGNER_UILIB_EXPORT "+n) { + + if strings.Contains(l, n+" ") || strings.Contains(l, n+":") || strings.HasSuffix(l, n) { + + l = normalizedClassDeclaration(f, i) + + if !strings.Contains(l, ":") { + return "" + } + + if strings.Contains(l, "<") { + l = strings.Split(l, "<")[0] + } + + if strings.Contains(l, "/") { + l = strings.Split(l, "/")[0] + } + + var tmp = strings.Split(l, ":")[1] + + for _, s := range []string{"{", "}", "#ifndef", "QT_NO_QOBJECT", "#else", "#endif", "class", "Q_" + strings.ToUpper(strings.TrimPrefix(m, "Qt")) + "_EXPORT " + n, "public", "protected", "private", " ", " "} { + tmp = strings.Replace(tmp, s, "", -1) + } + + return strings.TrimSpace(tmp) + } + } + } + + for _, l := range strings.Split(f, "\n") { + if strings.Contains(l, "struct "+n) || strings.Contains(l, "struct Q_"+strings.ToUpper(strings.TrimPrefix(m, "Qt"))+"_EXPORT "+n) { + return "" + } + } + + for _, l := range strings.Split(f, "\n") { + if strings.Contains(l, "namespace "+n) { + return "" + } + } + + utils.Log.WithField("module", strings.TrimPrefix(m, "Qt")).WithField("class", n).Debugln("failed to parse header") + return "" +} + +func normalizedClassDeclaration(f string, is int) string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + for i, l := range strings.Split(f, "\n") { + if i >= is { + fmt.Fprint(bb, l) + if strings.Contains(l, "{") { + break + } + } + } + return bb.String() +} diff --git a/qt/tool-chain/binding/parser/class_remove.go b/qt/tool-chain/binding/parser/class_remove.go new file mode 100644 index 0000000..ca9d8e5 --- /dev/null +++ b/qt/tool-chain/binding/parser/class_remove.go @@ -0,0 +1,159 @@ +package parser + +import ( + "strings" +) + +func (c *Class) remove() { + c.removeFunctions() + c.removeFunctions_Version() + + c.removeEnums() + c.removeEnums_Version() + + c.removeBases() +} + +func (c *Class) removeFunctions() { + for i := len(c.Functions) - 1; i >= 0; i-- { + f := c.Functions[i] + + switch { + case (f.Status == "obsolete" || f.Status == "compat") || + !(f.Access == "public" || f.Access == "protected") || + strings.ContainsAny(f.Name, "&<>=/!()[]{}^|*+-") || + strings.Contains(f.Name, "Operator"): + { + c.Functions = append(c.Functions[:i], c.Functions[i+1:]...) + } + + case (f.Virtual == IMPURE || f.Virtual == PURE) && f.Meta == CONSTRUCTOR: + { + c.Functions = append(c.Functions[:i], c.Functions[i+1:]...) + } + } + } +} + +func (c *Class) removeFunctions_Version() { + for i := len(c.Functions) - 1; i >= 0; i-- { + switch c.Functions[i].Fullname { + case "QTextBrowser::isModified", "QTextBrowser::setModified": + { + c.Functions = append(c.Functions[:i], c.Functions[i+1:]...) + } + + case "QSemaphoreReleaser::QSemaphoreReleaser": + { + if c.Functions[i].OverloadNumber == "4" { + c.Functions = append(c.Functions[:i], c.Functions[i+1:]...) + } + } + } + } +} + +func (c *Class) removeEnums() { + for i := len(c.Enums) - 1; i >= 0; i-- { + if e := c.Enums[i]; (e.Status == "obsolete" || e.Status == "compat") || + !(e.Access == "public" || e.Access == "protected") { + + c.Enums = append(c.Enums[:i], c.Enums[i+1:]...) + } + } +} + +func (c *Class) removeEnums_Version() { + for i := len(c.Enums) - 1; i >= 0; i-- { + switch c.Enums[i].ClassName() { + case "QCss", "QScript", "Http2": + { + c.Enums = append(c.Enums[:i], c.Enums[i+1:]...) + continue + } + } + switch e := c.Enums[i]; e.Fullname { + case "QTimeZone::anonymous": + { + c.Enums = append(c.Enums[:i], c.Enums[i+1:]...) + continue + } + case "Qt::InputMethodQuery": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if e.Values[iv].Name == "ImQueryInput" { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + break + } + } + continue + } + case "QV4::PropertyFlag": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if e.Values[iv].Name == "Attr_ReadOnly" || + e.Values[iv].Name == "Attr_ReadOnly_ButConfigurable" { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + } + } + continue + } + case "QDBusConnection::RegisterOption": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if strings.HasPrefix(e.Values[iv].Name, "ExportAll") { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + } + } + continue + } + case "QDateTimeEdit::Section": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if e.Values[iv].Name == "TimeSections_Mask" || + e.Values[iv].Name == "DateSections_Mask" { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + } + } + continue + } + case "QDockWidget::DockWidgetFeature": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if e.Values[iv].Name == "AllDockWidgetFeatures" { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + } + } + continue + } + case "QSGNode::DirtyStateBit": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if e.Values[iv].Name == "DirtyPropagationMask" { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + } + } + continue + } + case "QWebEnginePage::WebAction": + { + for iv := len(e.Values) - 1; iv >= 0; iv-- { + if e.Values[iv].Name == "NoWebAction" { + c.Enums[i].Values = append(c.Enums[i].Values[:iv], c.Enums[i].Values[iv+1:]...) + } + } + continue + } + } + } +} + +func (c *Class) removeBases() { + var bases = c.GetBases() + for i := len(bases) - 1; i >= 0; i-- { + if _, ok := State.ClassMap[bases[i]]; !ok { + bases = append(bases[:i], bases[i+1:]...) + } + } + c.Bases = strings.Join(bases, ",") +} diff --git a/qt/tool-chain/binding/parser/enum.go b/qt/tool-chain/binding/parser/enum.go new file mode 100644 index 0000000..9146357 --- /dev/null +++ b/qt/tool-chain/binding/parser/enum.go @@ -0,0 +1,42 @@ +package parser + +import "strings" + +type Enum struct { + Name string `xml:"name,attr"` + Fullname string `xml:"fullname,attr"` + Status string `xml:"status,attr"` + Access string `xml:"access,attr"` + Typedef string `xml:"typedef,attr"` + Values []*Value `xml:"value"` + NoConst bool +} + +type Value struct { + Name string `xml:"name,attr"` + Value string `xml:"value,attr"` +} + +func (e *Enum) Class() (*Class, bool) { + var class, ok = State.ClassMap[e.ClassName()] + return class, ok +} + +func (e *Enum) ClassName() string { + return strings.Split(e.Fullname, "::")[0] +} + +func (e *Enum) register(m string) { + + if c, ok := e.Class(); !ok { + State.ClassMap[e.ClassName()] = &Class{ + Name: e.ClassName(), + Status: "commendable", + Module: m, + Access: "public", + Enums: []*Enum{e}, + } + } else { + c.Enums = append(c.Enums, e) + } +} diff --git a/qt/tool-chain/binding/parser/function.go b/qt/tool-chain/binding/parser/function.go new file mode 100644 index 0000000..7ce1458 --- /dev/null +++ b/qt/tool-chain/binding/parser/function.go @@ -0,0 +1,738 @@ +package parser + +import ( + "fmt" + "sort" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +type Function struct { + Name string `xml:"name,attr"` + Fullname string `xml:"fullname,attr"` + Href string `xml:"href,attr"` + Status string `xml:"status,attr"` + Access string `xml:"access,attr"` + Filepath string `xml:"filepath,attr"` + Virtual string `xml:"virtual,attr"` + Meta string `xml:"meta,attr"` + Static bool `xml:"static,attr"` + Overload bool `xml:"overload,attr"` + OverloadNumber string `xml:"overload-number,attr"` + Output string `xml:"type,attr"` + Signature string `xml:"signature,attr"` + Parameters []*Parameter `xml:"parameter"` + Brief string `xml:"brief,attr"` + Since string `xml:"since,attr"` + SignalMode string + TemplateModeJNI string + Default bool + TmpName string + Export bool + NeedsFinalizer bool + Container string + TemplateModeGo string + NonMember bool + NoMocDeduce bool + Synthetic bool + Checked bool + Exception bool + IsMap bool + OgParameters []Parameter + IsMocFunction bool + IsMocProperty bool + PureGoOutput string + Connect int + Target string + Inbound bool + BoundByEmscripten bool //TODO: needed at all ? + FakeForJSCallback bool +} + +type Parameter struct { + Name string `xml:"name,attr"` + Value string `xml:"left,attr"` + ValueNew string `xml:"type,attr"` + Right string `xml:"right,attr"` + Default string `xml:"default,attr"` + PureGoType string +} + +func (f *Function) Class() (*Class, bool) { + var class, ok = State.ClassMap[f.ClassName()] + return class, ok +} + +func (f *Function) ClassName() string { + var s = strings.Split(f.Fullname, "::") + if len(s) == 3 { + return s[1] + } + return s[0] +} + +func (f *Function) register(m string) { + + if c, ok := f.Class(); !ok { + State.ClassMap[f.ClassName()] = &Class{ + Name: f.ClassName(), + Status: "commendable", + Module: m, + Access: "public", + Functions: []*Function{f}, + } + } else { + c.Functions = append(c.Functions, f) + } +} + +//TODO: multipoly [][]string +//TODO: connect/disconnect slot functions + add necessary SIGNAL_* functions (check first if really needed) +func (f *Function) PossiblePolymorphicDerivations(self bool) ([]string, string) { + var out = make([]string, 0) + + var params = func() []*Parameter { + if self { + return []*Parameter{{Name: "ptr", Value: f.ClassName()}} + } + return f.Parameters + }() + + for _, p := range params { + var c, ok = State.ClassMap[CleanValue(p.Value)] + if !ok { + continue + } + + if f.Meta == CONSTRUCTOR { + for _, class := range State.ClassMap { + //TODO: use target to block certain classes + if ShouldBuildForTarget(strings.TrimPrefix(class.Module, "Qt"), State.Target) && + !(class.Name == "QCameraViewfinder" || class.Name == "QGraphicsVideoItem" || + class.Name == "QVideoWidget" || class.Name == "QVideoWidgetControl") { + if class.IsPolymorphic() && class.IsSubClassOf(c.Name) && class.IsSupported() { + out = append(out, class.Name) + } + } + } + } else { + var fc, _ = f.Class() + for _, class := range SortedClassesForModule(fc.Module, false) { + if class.IsPolymorphic() && class.IsSubClassOf(c.Name) && class.IsSupported() { + out = append(out, class.Name) + } + } + } + + //TODO: multipoly + if len(out) > 0 { + sort.Stable(sort.StringSlice(out)) + out = append(out, c.Name) + return out, CleanName(p.Name, p.Value) + } + } + + return out, "" +} + +func (f *Function) PossibleDerivationsReversedAndRemovedPure(self bool) ([]string, string) { + if self { + var fc, ok = f.Class() + if !ok { + return make([]string, 0), "" + } + var derv = fc.GetAllDerivationsInSameModule() + var out = make([]string, 0) + for i := len(derv); i > 0; i-- { + if !(derv[i-1] == "QAbstractButton" && f.Name == "paintEvent") { + if c, ok := State.ClassMap[derv[i-1]]; ok && c.IsSupported() { + out = append(out, derv[i-1]) + } + } + } + out = append(out, fc.Name) + return out, "" + } + + var out = make([]string, 0) + + var params = func() []*Parameter { + if self { + return []*Parameter{{Name: "ptr", Value: f.ClassName()}} + } + return f.Parameters + }() + + var fc, _ = f.Class() + + for _, p := range params { + var c, ok = State.ClassMap[CleanValue(p.Value)] + if !ok { + continue + } + + for _, class := range SortedClassesForModule(fc.Module, false) { + if class.IsSubClassOf(c.Name) && class.IsSupported() && + !(class.Name == "QAbstractButton" && f.Name == "paintEvent") { + out = append(out, class.Name) + } + } + + //TODO: multipoly + if len(out) > 0 { + sort.Stable(sort.StringSlice(out)) + out = append(out, c.Name) + return out, CleanName(p.Name, p.Value) + } + } + + return out, "" +} + +func (f *Function) PossibleDerivationsInAllModules(self bool) ([]string, string) { + var out = make([]string, 0) + + var params = func() []*Parameter { + if self { + return []*Parameter{{Name: "ptr", Value: f.ClassName()}} + } + return f.Parameters + }() + + for _, p := range params { + var c, ok = State.ClassMap[CleanValue(p.Value)] + if !ok { + continue + } + + for _, class := range State.ClassMap { + if class.IsSubClassOf(c.Name) && class.IsSupported() && + !(class.Name == "QAbstractButton" && f.Name == "paintEvent") { + out = append(out, class.Name) + } + } + + //TODO: multipoly + if len(out) > 0 { + sort.Stable(sort.StringSlice(out)) + out = append(out, c.Name) + return out, CleanName(p.Name, p.Value) + } + } + + return out, "" +} + +func (f *Function) IsJNIGeneric() bool { + + if f.ClassName() == "QAndroidJniObject" { + switch f.Name { + case + "callMethod", + "callStaticMethod", + + "getField", + //"setField", -> uses interface{} if not generic + + "getStaticField", + //"setStaticField", -> uses interface{} if not generic + + "getObjectField", + + "getStaticObjectField", + + "callObjectMethod", + "callStaticObjectMethod": + { + return true + } + + case "setStaticField": + { + if f.OverloadNumber == "2" || f.OverloadNumber == "4" { + return true + } + } + } + } + + return false +} + +//TODO: +func (f *Function) IsSupported() bool { + + if utils.QT_MACPORTS() { + if f.Fullname == "QWebFrame::ownerElement" || f.Fullname == "QWebHistory::toMap" || + f.Fullname == "QWebHistoryItem::toMap" || f.Fullname == "QWebPage::consoleMessageReceived" || + f.Fullname == "QWebPage::focusedElementChanged" || f.Fullname == "QWebPage::recentlyAudibleChanged" || + f.Fullname == "QWebPage::recentlyAudible" || f.Fullname == "QWebSettings::pluginSearchPaths" || + f.Fullname == "QWebSettings::setPluginSearchPaths" { + if !strings.Contains(f.Access, "unsupported") { + f.Access = "unsupported_isBlockedFunction" + } + return false + } + } + + if utils.QT_VERSION_NUM() >= 5080 { + if f.Fullname == "QJSEngine::newQMetaObject" && f.OverloadNumber == "2" || + f.Fullname == "QScxmlTableData::instructions" || f.Fullname == "QScxmlTableData::dataNames" || + f.Fullname == "QScxmlTableData::stateMachineTable" || + f.Fullname == "QTextToSpeech::voiceChanged" { + if !strings.Contains(f.Access, "unsupported") { + f.Access = "unsupported_isBlockedFunction" + } + return false + } + } + + switch { + case + f.ClassName() == "operator QCborError", + + (f.ClassName() == "QAccessibleObject" || f.ClassName() == "QAccessibleInterface" || f.ClassName() == "QAccessibleWidget" || //QAccessible::State -> quint64 + f.ClassName() == "QAccessibleStateChangeEvent") && (f.Name == "state" || f.Name == "changedStates" || f.Name == "m_changedStates" || f.Name == "setM_changedStates" || f.Meta == CONSTRUCTOR), + + f.Fullname == "QPixmapCache::find" && f.OverloadNumber == "4", //Qt::Key -> int + (f.Fullname == "QPixmapCache::remove" || f.Fullname == "QPixmapCache::insert") && f.OverloadNumber == "2", + f.Fullname == "QPixmapCache::replace", + + f.Fullname == "QNdefFilter::appendRecord" && !f.Overload, //QNdefRecord::TypeNameFormat -> uint + + f.ClassName() == "QSimpleXmlNodeModel" && f.Meta == CONSTRUCTOR, + + f.Fullname == "QSGMaterialShader::attributeNames", + + f.ClassName() == "QVariant" && (f.Name == "value" || f.Name == "canConvert"), //needs template + + f.Fullname == "QNdefRecord::isRecordType", f.Fullname == "QScriptEngine::scriptValueFromQMetaObject", //needs template + f.Fullname == "QScriptEngine::fromScriptValue", f.Fullname == "QJSEngine::fromScriptValue", + + f.ClassName() == "QMetaType" && //needs template + (f.Name == "hasRegisteredComparators" || f.Name == "registerComparators" || + f.Name == "hasRegisteredConverterFunction" || f.Name == "registerConverter" || + f.Name == "registerEqualsComparator"), + + State.ClassMap[f.ClassName()].Module == MOC && f.Name == "metaObject", //needed for qtmoc + + f.Fullname == "QSignalBlocker::QSignalBlocker" && f.OverloadNumber == "3", //undefined symbol + + (State.ClassMap[f.ClassName()].IsSubClassOf("QCoreApplication") || + f.ClassName() == "QAudioInput" || f.ClassName() == "QAudioOutput") && f.Name == "notify", //redeclared (name collision with QObject) + + f.Fullname == "QGraphicsItem::isBlockedByModalPanel", //** problem + + f.Name == "surfaceHandle", //QQuickWindow && QQuickView //unsupported_cppType(QPlatformSurface) + + f.Name == "QDesignerFormWindowInterface" || f.Name == "QDesignerFormWindowManagerInterface" || f.Name == "QDesignerWidgetBoxInterface", //unimplemented virtual + + f.Fullname == "QNdefNfcSmartPosterRecord::titleRecords", //T output with unsupported output for *_atList + f.Fullname == "QHelpEngineCore::filterAttributeSets", f.Fullname == "QHelpSearchEngine::query", f.Fullname == "QHelpSearchQueryWidget::query", + f.Fullname == "QPluginLoader::staticPlugins", f.Fullname == "QSslConfiguration::ellipticCurves", f.Fullname == "QSslConfiguration::supportedEllipticCurves", + f.Fullname == "QTextFormat::lengthVectorProperty", f.Fullname == "QTextTableFormat::columnWidthConstraints", f.Fullname == "QHelpContentWidget::selectedIndexes", + + f.Fullname == "QListView::indexesMoved", f.Fullname == "QAudioInputSelectorControl::availableInputs", f.Fullname == "QScxmlStateMachine::initialValuesChanged", + f.Fullname == "QAudioOutputSelectorControl::availableOutputs", f.Fullname == "QQuickWebEngineProfile::downloadFinished", + f.Fullname == "QQuickWindow::closing", f.Fullname == "QQuickWebEngineProfile::downloadRequested", + + f.Fullname == "QApplication::autoMaximizeThreshold", f.Fullname == "QApplication::setAutoMaximizeThreshold", + + f.Fullname == "QWebPluginFactory::__plugins_newList", + + f.Fullname == "QWebHistoryItem::loadFromMap", f.Fullname == "QWebHistory::loadFromMap", + + f.Name == "QCanBusDeviceInfo", f.Fullname == "QRemoteObjectNode::instances" && !f.Overload, + + f.Fullname == "QtROClientFactory::registerType", f.Fullname == "QtROServerFactory::registerType", + f.Name == "QtROClientFactory", f.Name == "QtROServerFactory", + + f.Name == "glShaderSource", //OpenGL + + f.Name == "qt_test_iobluetooth_runloop", + + f.Name == "setVulkanInstance", f.Name == "vulkanInstance", + + f.Name == "QRandomGenerator" && f.OverloadNumber == "4", + + f.Fullname == "QAndroidBinder::onTransact", f.Fullname == "QtAndroid::checkPermission", + + UseJs() && + (strings.Contains(f.Name, "ibraryPath") || f.Fullname == "QLockFile::getLockInfo" || + f.Name == "inputMethodEvent" || f.Name == "updateInputMethod" || f.Name == "inputMethodQuery" || + f.Fullname == "QHeaderView::isFirstSectionMovable" || f.Fullname == "QXmlSimpleReader::property" || f.Fullname == "QXmlReader::property" || + f.Fullname == "QWebSocket::ignoreSslErrors" || f.Fullname == "QWebSocket::preSharedKeyAuthenticationRequired" || + f.Fullname == "QWebSocket::sslConfiguration" || f.Fullname == "QWebSocket::setSslConfiguration" || + f.Fullname == "QWebSocketServer::peerVerifyError" || (strings.HasPrefix(f.ClassName(), "QWeb") && strings.Contains(f.Name, "slErrors")) || + f.Fullname == "QWebSocketServer::preSharedKeyAuthenticationRequired" || f.Fullname == "QWebSocketServer::setSslConfiguration" || f.Fullname == "QWebSocketServer::sslConfiguration" || + (f.Name == "readData" && len(f.Parameters) == 2)), + + f.Name == "qt_metacast", f.Fullname == "QVariant::fromStdVariant", + f.Name == "qt_check_for_QGADGET_macro", + + strings.HasSuffix(f.Name, "_ptr"), + f.ClassName() == "QPixmap" && (f.Name == "setAlphaChannel" || f.Name == "alphaChannel"), + f.Fullname == "QTabletEvent::hiResGlobalPos", + + f.Name == "QOpenGLPaintDevice" && f.OverloadNumber == "5", + + f.Name == "d", f.Name == "setD", + + f.Fullname == "QAbstractItemModelTester::failureReportingMode", + + f.Fullname == "QtRemoteObjects::qt_getEnumMetaObject", + + //WebEngine + f.Fullname == "QWebEnginePage::quotaRequested", + f.Fullname == "QWebEnginePage::registerProtocolHandlerRequested", + f.Fullname == "QWebEnginePage::save", + f.Fullname == "QWebEnginePage::fullScreenRequested", + + f.Fullname == "QWebEngineScriptCollection::insert", + f.Fullname == "QWebEngineScriptCollection::findScript", + f.Fullname == "QWebEngineScriptCollection::remove", + f.Fullname == "QWebEngineScriptCollection::contains", + f.Fullname == "QWebEngineScriptCollection::findScripts", + f.Fullname == "QWebEngineScriptCollection::toList", + + f.Fullname == "QWebEngineView::pageAction", + f.Fullname == "QWebEngineView::createWindow", + f.Fullname == "QWebEngineView::renderProcessTerminated", + f.Fullname == "QWebEngineView::triggerPageAction", + // + + f.Fullname == "QCustom3DVolume::QCustom3DVolume" && f.OverloadNumber == "2", + + f.Name == "defaultDtlsConfiguration", f.Name == "setDefaultDtlsConfiguration", + f.Name == "setDtlsCookieVerificationEnabled", f.Name == "dtlsCookieVerificationEnabled", + f.Fullname == "QNearFieldManager::adapterStateChanged", f.Name == "singletonInstance", + f.Fullname == "QWebEngineUrlScheme::syntax", + + strings.Contains(f.Access, "unsupported"): + { + if !strings.Contains(f.Access, "unsupported") { + f.Access = "unsupported_isBlockedFunction" + } + return false + } + } + + if f.Name == "__draw_selections_newList" { + return false + } + + //generic blocked + //TODO: also check _setList _atList _newList _keyList instead ? + genName := strings.TrimPrefix(f.Name, "__") + if strings.HasPrefix(genName, "registeredTimers") || strings.HasPrefix(genName, "countriesForLanguage") || + strings.HasPrefix(genName, "writingSystem") || strings.HasPrefix(genName, "textList") || + strings.HasPrefix(genName, "attributes") || strings.HasPrefix(genName, "additionalFormats") || + strings.HasPrefix(genName, "rawHeaderPairs") || strings.HasPrefix(genName, "tabs") || + strings.HasPrefix(genName, "QInputMethodEvent_attributes") || strings.HasPrefix(genName, "selections") || strings.HasPrefix(genName, "setSelections") || + strings.HasPrefix(genName, "setAdditionalFormats") || strings.HasPrefix(genName, "setFormats") || + strings.HasPrefix(genName, "setTabs") || strings.HasPrefix(genName, "extraSelections") || + strings.HasPrefix(genName, "setExtraSelections") || strings.HasPrefix(genName, "setButtonLayout") || + strings.HasPrefix(genName, "setWhiteList") || strings.HasPrefix(genName, "whiteList") || + strings.HasPrefix(genName, "supportedViewfinderFrameRateRanges") || strings.HasPrefix(genName, "hits") || + strings.HasPrefix(genName, "featureTypes") || strings.HasPrefix(genName, "supportedPaperSources") || + strings.HasPrefix(genName, "setTextureData") || strings.HasPrefix(genName, "textureData") || + strings.HasPrefix(genName, "QCustom3DVolume_textureData") || strings.HasPrefix(genName, "createTextureData") || + strings.Contains(genName, "alternateSubjectNames") || strings.HasPrefix(genName, "fromVariantMap") || + strings.HasPrefix(genName, "QScxmlDataModel") || strings.HasPrefix(genName, "readAllFrames") || + strings.HasPrefix(genName, "manufacturerData") { + + if strings.HasPrefix(genName, "setTabs") || strings.HasPrefix(genName, "tabs") { + return !strings.HasPrefix(f.Name, "__") + } + + return false + } + + //TODO: + if f.Name == "nativeEvent" { + f.Access = "unsupported_isBlockedFunction" + return false + } + + //TODO: blocked for small + if f.Fullname == "QTemporaryFile::open" && f.OverloadNumber == "2" || + f.Fullname == "QXmlEntityResolver::resolveEntity" || + f.Fullname == "QXmlReader::parse" && f.OverloadNumber == "2" || + f.Fullname == "QGraphicsItem::updateMicroFocus" || + f.Fullname == "QSvgGenerator::metric" || + f.Fullname == "QScxmlDataModel::setScxmlEvent" || + f.Fullname == "QPageSetupDialog::open" || + f.Fullname == "QPrintPreviewDialog::open" || + f.Fullname == "QSqlRelationalTableModel::revert" || + f.Fullname == "QSqlRelationalTableModel::submit" || + f.Fullname == "QSqlTableModel::revert" || + f.Fullname == "QSqlTableModel::submit" || + f.Fullname == "QFormLayout::itemAt" || + f.Fullname == "QGraphicsGridLayout::itemAt" || + + ((f.ClassName() == "QGraphicsGridLayout" || f.ClassName() == "QFormLayout") && f.Name == "itemAt" && f.OverloadNumber == "2") { + return false + } + + if utils.QT_VERSION_NUM() <= 5042 { + if f.Fullname == "QIODevice::open" && f.OverloadNumber == "3" || + f.Fullname == "QImage::QImage" && (f.OverloadNumber == "11" || f.OverloadNumber == "12") || + f.Fullname == "QGraphicsLayout::invalidate" || + f.Fullname == "QAudioRoleControl::supportedAudioRoles" { + f.Access = "unsupported_isBlockedFunction" + return false + } + } + + if State.Minimal { + return f.Export || f.Meta == DESTRUCTOR || f.Fullname == "QObject::destroyed" || strings.HasPrefix(f.Name, TILDE) + } + + return true +} + +func IsBlockedDefault() []string { + return []string{ + "QAnimationGroup::updateCurrentTime", + "QAnimationGroup::duration", + "QAbstractProxyModel::columnCount", + "QAbstractTableModel::columnCount", + "QAbstractListModel::data", + "QAbstractTableModel::data", + "QAbstractProxyModel::index", + "QAbstractProxyModel::parent", + "QAbstractListModel::rowCount", + "QAbstractProxyModel::rowCount", + "QAbstractTableModel::rowCount", + + "QNetworkReply::readData", + + "QPagedPaintDevice::paintEngine", + "QAccessibleObject::childCount", + "QAccessibleObject::indexOfChild", + "QAccessibleObject::role", + "QAccessibleObject::text", + "QAccessibleObject::child", + "QAccessibleObject::parent", + "QAbstractGraphicsShapeItem::paint", + "QGraphicsObject::paint", + "QLayout::sizeHint", + "QAbstractGraphicsShapeItem::boundingRect", + "QGraphicsObject::boundingRect", + "QGraphicsLayout::sizeHint", + + "QSimpleXmlNodeModel::typedValue", + "QSimpleXmlNodeModel::documentUri", + "QSimpleXmlNodeModel::compareOrder", + "QSimpleXmlNodeModel::nextFromSimpleAxis", + "QSimpleXmlNodeModel::kind", + "QSimpleXmlNodeModel::name", + "QSimpleXmlNodeModel::root", + + "QAbstractPlanarVideoBuffer::unmap", + "QAbstractPlanarVideoBuffer::mapMode", + + "QSGDynamicTexture::bind", + "QSGDynamicTexture::hasMipmaps", + "QSGDynamicTexture::textureSize", + "QSGDynamicTexture::hasAlphaChannel", + "QSGDynamicTexture::textureId", + + "QModbusClient::open", + "QModbusServer::open", + "QModbusClient::close", + "QModbusServer::close", + + "QAbstractBarSeries::type", + "QXYSeries::type", + } +} + +//TODO: combine +func (f *Function) IsDerivedFromVirtual() bool { + if f.Virtual != "non" { + return true + } + + var class, ok = f.Class() + if !ok { + //return false + } + + for _, bc := range class.GetAllBases() { + if bclass, ok := State.ClassMap[bc]; ok { + + for _, cf := range bclass.Functions { + if cf.Name == f.Name && + + cf.Output == f.Output && len(cf.Parameters) == len(f.Parameters) && + cf.Virtual != "non" { + + var similar = true + for i, cfp := range cf.Parameters { + if cfp.Value != f.Parameters[i].Value { + similar = false + } + } + if similar { + return true + } + } + } + + } + } + + return false +} + +//TODO: combine +func (f *Function) IsDerivedFromImpure() bool { + if f.Static || f.Virtual == PURE { + return false + } + + var class, ok = f.Class() + if !ok { + //return false + } + + if f.Virtual == IMPURE { + return true + } + + for _, bc := range class.GetAllBases() { + if bclass, ok := State.ClassMap[bc]; ok { + + for _, cf := range bclass.Functions { + if cf.Name == f.Name && + + cf.Output == f.Output && len(cf.Parameters) == len(f.Parameters) && + cf.Virtual == IMPURE { + + var similar = true + for i, cfp := range cf.Parameters { + if cfp.Value != f.Parameters[i].Value { + similar = false + } + } + if similar { + return true + } + } + } + + } + } + + return false +} + +func (f *Function) IsDerivedFromPure() bool { + var class, ok = f.Class() + if !ok { + //return false + } + + if f.Virtual == PURE { + return true + } + + for _, bc := range class.GetAllBases() { + if bclass, ok := State.ClassMap[bc]; ok { + + for _, cf := range bclass.Functions { + if cf.Name == f.Name && + + cf.Output == f.Output && len(cf.Parameters) == len(f.Parameters) && + cf.Virtual == PURE { + + var similar = true + for i, cfp := range cf.Parameters { + if cfp.Value != f.Parameters[i].Value { + similar = false + } + } + if similar { + return true + } + } + } + + } + } + + return false +} + +func (f *Function) FindDeepestImplementation() string { + var c, _ = f.Class() + + for _, bcn := range c.GetBases() { + var bc, ok = State.ClassMap[bcn] + if !ok { + continue + } + + var f = *f + f.Fullname = fmt.Sprintf("%v::%v", bcn, f.Name) + var out = f.FindDeepestImplementation() + if out != "" { + if c.Module != bc.Module { + if f.SignalMode == CALLBACK || f.Default || f.Static || strings.HasPrefix(f.Name, "__") { + return c.Name + } + + //TODO: ---> + if strings.HasPrefix(f.Name, "__") { + if f.Root().IsDerivedFromVirtual() { + return c.Name + } + } + //<-- + + f.Fullname = fmt.Sprintf("%v::%v", c.Name, f.Name) + if plist, _ := f.PossiblePolymorphicDerivations(true); len(plist) > 0 { + return c.Name + } + } + var lf = bc.GetFunction(f.Name) + if lf != nil && lf.Virtual == PURE { + return c.Name + } + return out + } + } + + if c.HasFunction(f) { + return c.Name + } + + return "" +} + +func (f *Function) Implements() bool { + return f.TemplateModeGo != "" || f.FindDeepestImplementation() == f.ClassName() +} + +func (f *Function) Root() *Function { + var c, ok = f.Class() + if !ok || !strings.HasPrefix(f.Name, "__") { + return f + } + + for _, bcn := range c.GetAllBases() { + var bc, ok = State.ClassMap[bcn] + if !ok { + continue + } + for _, cf := range bc.Functions { + if cf.Name == strings.Split(strings.TrimPrefix(f.Name, "__"), "_")[0] && cf.OverloadNumber == f.OverloadNumber { + return cf + } + } + } + + return f +} diff --git a/qt/tool-chain/binding/parser/function_fix.go b/qt/tool-chain/binding/parser/function_fix.go new file mode 100644 index 0000000..551c607 --- /dev/null +++ b/qt/tool-chain/binding/parser/function_fix.go @@ -0,0 +1,696 @@ +package parser + +import ( + "fmt" + "strconv" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func (f *Function) fix() { + f.fixGeneral() + f.fixGeneral_Version() + + f.fixOverload() + f.fixOverload_Version() + + //f.fixGeneric() +} + +func (f *Function) fixGeneral() { + + if utils.QT_VERSION_NUM() >= 5110 { + for _, p := range f.Parameters { + if p.Value == "" { + p.Value = p.ValueNew + } + if p.Default == "nullptr" { + p.Default = "Q_NULLPTR" + } + } + } + + //linux fixes + + if f.Fullname == "QThread::start" { + f.Parameters = make([]*Parameter, 0) + } + + //virtual fixes + + if f.Virtual == "virtual" { + f.Virtual = IMPURE + } + + if f.Virtual == IMPURE || f.Virtual == PURE || + f.Meta == SIGNAL || f.Meta == SLOT { + + f.Static = false + } + + //constructor fixes + + if f.Meta == COPY_CONSTRUCTOR || f.Meta == MOVE_CONSTRUCTOR { + f.Meta = CONSTRUCTOR + } + + var class, ok = f.Class() + if !ok || !class.isSubClass() { + return + } + + if f.Meta == CONSTRUCTOR { + f.Status = "active" + f.Access = "public" + } +} + +func (f *Function) fixGeneral_AfterClasses() { + if f.Name != "open" && f.Name != "setGeometry" && f.Name != "setScxmlEvent" && //TODO: + !f.Static && f.Virtual == "non" && f.Meta == PLAIN && f.IsDerivedFromVirtual() { + f.Virtual = IMPURE + } +} + +func (f *Function) fixGeneral_Version() { + switch f.Fullname { + case "QScxmlCppDataModel::setScxmlEvent": + { + f.Virtual = "non" + } + + case "QGraphicsObject::z", "QGraphicsObject::setZ": + { + f.Name = func() string { + if f.Name == "setZ" { + return "setZValue" + } + return "zValue" + }() + f.Fullname = fmt.Sprintf("%v::%v", f.ClassName(), f.Name) + } + + case "QGraphicsObject::effect", "QGraphicsObject::setEffect": + { + f.Name = func() string { + if f.Name == "setEffect" { + return "setGraphicsEffect" + } + return "graphicsEffect" + }() + f.Fullname = fmt.Sprintf("%v::%v", f.ClassName(), f.Name) + } + + case "QOperatingSystemVersion::QOperatingSystemVersion": + { + if len(f.Parameters) > 1 { + f.OverloadNumber = "2" + f.Overload = true + } + } + case "QAndroidJniEnvironment::javaVM": + { + f.Output = strings.Replace(f.Output, "int *", "JavaVM *", -1) + f.Signature = strings.Replace(f.Signature, "int *", "JavaVM *", -1) + } + case "QAndroidJniObject::fromLocalRef": + { + f.Parameters[0].Value = strings.Replace(f.Parameters[0].Value, "int", "jobject", -1) + f.Signature = strings.Replace(f.Signature, "int", "jobject", -1) + } + case "QAndroidJniObject::QAndroidJniObject": + { + if strings.HasSuffix(f.Href, "5") { + f.Parameters[0].Value = strings.Replace(f.Parameters[0].Value, "int", "jobject", -1) + f.Signature = strings.Replace(f.Signature, "int", "jobject", -1) + } + } + case "QImage::QImage": + { + for i := len(f.Parameters) - 1; i >= 0; i-- { + if strings.HasPrefix(f.Parameters[i].Name, "cleanup") { + f.Parameters = append(f.Parameters[:i], f.Parameters[i+1:]...) + } + } + } + case "QWebEnginePage::certificateError": + { + f.Signature = strings.Replace(f.Signature, "const int", "const QWebEngineCertificateError", -1) + f.Parameters[0].Value = strings.Replace(f.Parameters[0].Value, "const int", "const QWebEngineCertificateError", -1) + } + case "QTextLayout::formats": + { + f.Output = "QVector" + } + case "QWebEnginePage::print": + { + f.Parameters = append(f.Parameters[:len(f.Parameters)-1], f.Parameters[len(f.Parameters):]...) + } + } +} + +func (f *Function) fixOverload() { + + if strings.Contains(f.Href, "-") { + tmp, err := strconv.Atoi(strings.Split(f.Href, "-")[1]) + if err == nil && tmp > 0 { + f.Overload = true + f.OverloadNumber = strconv.Itoa(tmp + 1) + } + } + + if f.OverloadNumber == "1" { + f.OverloadNumber = "2" + } + + if f.OverloadNumber != "0" { + return + } + + f.Overload = false + f.OverloadNumber = "" +} + +func (f *Function) fixOverload_Version() { + switch f.Fullname { + case "QGraphicsDropShadowEffect::setOffset", "QGraphicsScene::setSceneRect", + "QGraphicsView::setSceneRect", "QQuickItem::setFocus", + "QAccessibleWidget::setText", "QSvgGenerator::setViewBox", + "QSvgRenderer::setViewBox": + { + var class, ok = f.Class() + if !ok { + return + } + + var count int + for _, sf := range class.Functions { + if sf.Fullname != f.Fullname { + continue + } + + if sf.Signature != f.Signature { + count++ + continue + } + + break + } + if count == 0 { + return + } + + f.Overload = true + f.OverloadNumber = strconv.Itoa(count + 1) + } + } +} + +func (f *Function) fixGeneric() { + f.fixGenericOutput() + f.fixGenericInput() +} + +func (f *Function) fixGenericOutput() { + + switch CleanValue(f.Output) { + case "QVariantHash": + { + f.Output = "QHash" + } + + case "QVariantMap": + { + f.Output = "QMap" + } + + case "QJSValueList": + { + f.Output = "QList" + } + + case "QModelIndexList": + { + f.Output = "QList" + } + + case "QVariantList": + { + f.Output = "QList" + } + + case "QObjectList": + { + f.Output = "QList" + } + + case "QMediaResourceList": + { + f.Output = "QList" + } + + case "QFileInfoList": + { + f.Output = "QList" + } + + case "QWidgetList": + { + f.Output = "QList" + } + + case "QCameraFocusZoneList": + { + //f.Output = "QList" //TODO: uncomment + } + + case "QList": + { + f.TemplateModeGo = "QObject*" + f.Output = "QList" + } + + case "QVector": + { + f.Output = "QList" + } + + case "T": + { + switch className := f.ClassName(); className { + case "QObject", "QMediaService": + { + f.TemplateModeGo = fmt.Sprintf("%v*", className) + f.Output = fmt.Sprintf("%v*", className) + } + } + } + } +} + +func (f *Function) fixGenericInput() { + var skipOG bool + for _, p := range f.Parameters { + if strings.HasPrefix(p.Value, "[]") || strings.HasPrefix(p.Value, "map[") { + skipOG = true + break + } + } + + if len(f.OgParameters) == 0 && !skipOG { + for _, p := range f.Parameters { + if p.Default == "..." { + switch f.Name { + case "QPaintEngine": + p.Default = "PaintEngineFeatures()" + case "QLayoutItem": + p.Default = "Qt::Alignment()" + case "QBluetoothLocalDevice", "QWebEngineUrlRequestInterceptor": + p.Default = "Q_NULLPTR" + case "QMediaPlayer", "QQuickImageProvider": + p.Default = "Flags()" + case "QModbusRequest", "QModbusResponse": + p.Default = "QByteArray()" + case "QGeoServiceProvider": + p.Default = "QVariantMap()" + default: + p.Default = "Qt::WindowFlags()" + } + } + if strings.HasPrefix(p.Default, "DECLARE_READING") { + p.Default = "" + } + f.OgParameters = append(f.OgParameters, *p) + } + } + + for _, p := range f.Parameters { + switch CleanValue(p.Value) { + case "QVariantHash": + { + p.Value = "QHash" + } + + case "QVariantMap": + { + p.Value = "QMap" + } + + case "QJSValueList": + { + p.Value = "QList" + } + + case "QModelIndexList": + { + p.Value = "QList" + } + + case "QVariantList": + { + p.Value = "QList" + } + + case "QObjectList": + { + p.Value = "QList" + } + + case "QMediaResourceList": + { + p.Value = "QList" + } + + case "QFileInfoList": + { + p.Value = "QList" + } + + case "QWidgetList": + { + p.Value = "QList" + } + + case "QCameraFocusZoneList": + { + //p.Value = "QList" //TODO: uncomment + } + + case "QList": + { + p.Value = "QList" + } + + case "QVector": + { + p.Value = "QList" + } + + case "T": + { + switch className := f.ClassName(); className { + case "QObject", "QMediaService": + { + p.Value = fmt.Sprintf("%v*", className) + } + } + } + } + } +} + +func (c *Class) FixGenericHelper() { + for _, cn := range append([]string{c.Name}, c.GetAllBases()...) { + var rec bool + + var class, e = State.ClassMap[cn] + if !e { + continue + } + for _, f := range class.Functions { + //TODO: needed because there could be unfixed subclasses; delay this to later (also check for GetAllBases or GetBases in parser) + f.fixGeneral() + f.fixGeneric() + + if IsPackedList(CleanValue(f.Output)) || IsPackedMap(CleanValue(f.Output)) { + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_atList", f.Name), f.OverloadNumber) { + var key, output, isMap = func() (string, string, bool) { + if IsPackedList(CleanValue(f.Output)) { + return "int", strings.Split(strings.Split(f.Output, "<")[1], ">")[0], false + } + var key, value = UnpackedMapDirty(CleanValue(f.Output)) + return key, value, true + }() + + params := []*Parameter{{Name: "i", Value: key}} + if isMap { + params[0].Name = "v" + params = append(params, &Parameter{Name: "i", Value: "int"}) + params[0].PureGoType = func() string { + if strings.Contains(f.PureGoOutput, "[error]") { + return "error" + } + return "" + }() + } else { + params[0].PureGoType = func() string { + if strings.Contains(f.PureGoOutput, "]error") { + return "error" + } + return "" + }() + } + + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_atList", f.Name), + Fullname: fmt.Sprintf("%v::__%v_atList", c.Name, f.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: output, + Parameters: params, + Signature: "()", + Container: strings.Split(f.Output, "<")[0], + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + PureGoOutput: func() string { + if strings.Contains(f.PureGoOutput, "]error") { + return "error" + } + return "" + }(), + IsMap: isMap, + }) + } + + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_setList", f.Name), f.OverloadNumber) { + var params = func() []*Parameter { + if IsPackedList(CleanValue(f.Output)) { + return []*Parameter{{Name: "i", Value: strings.Split(strings.Split(f.Output, "<")[1], ">")[0], + PureGoType: func() string { + if strings.Contains(f.PureGoOutput, "]error") { + return "error" + } + return "" + }()}} + } + var key, value = UnpackedMapDirty(CleanValue(f.Output)) + return []*Parameter{{Name: "key", Value: key, + PureGoType: func() string { + if strings.Contains(f.PureGoOutput, "[error]") { + return "error" + } + return "" + }()}, {Name: "i", Value: value, + PureGoType: func() string { + if strings.Contains(f.PureGoOutput, "]error") { + return "error" + } + return "" + }()}} + }() + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_setList", f.Name), + Fullname: fmt.Sprintf("%v::__%v_setList", c.Name, f.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "void", + Parameters: params, + Signature: "()", + Container: strings.Split(f.Output, "<")[0], + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + }) + } + + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_newList", f.Name), f.OverloadNumber) { + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_newList", f.Name), + Fullname: fmt.Sprintf("%v::__%v_newList", c.Name, f.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "void *", + Signature: "()", + Container: f.Output, + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + }) + } + + if IsPackedMap(CleanValue(f.Output)) { + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_keyList", f.Name), f.OverloadNumber) { + var keyType, _ = UnpackedMapDirty(CleanValue(f.Output)) + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_keyList", f.Name), + Fullname: fmt.Sprintf("%v::__%v_keyList", c.Name, f.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: fmt.Sprintf("QList<%v>", keyType), + Signature: "()", + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + PureGoOutput: func() string { + if strings.Contains(f.PureGoOutput, "[error]") { + return "[]error" + } + return "" + }(), + Container: CleanValue(f.Output), + }) + rec = true + } + } + } + + for _, p := range f.Parameters { + if IsPackedList(CleanValue(p.Value)) || IsPackedMap(CleanValue(p.Value)) { + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_%v_atList", f.Name, p.Name), f.OverloadNumber) { + var key, output, isMap = func() (string, string, bool) { + if IsPackedList(CleanValue(p.Value)) { + return "int", strings.Split(strings.Split(p.Value, "<")[1], ">")[0], false + } + var key, value = UnpackedMapDirty(CleanValue(p.Value)) + return key, value, true + }() + + params := []*Parameter{{Name: "i", Value: key}} + if isMap { + params[0].Name = "v" + params = append(params, &Parameter{Name: "i", Value: "int"}) + params[0].PureGoType = func() string { + if strings.Contains(p.PureGoType, "[error]") { + return "error" + } + return "" + }() + } else { + params[0].PureGoType = func() string { + if strings.Contains(p.PureGoType, "]error") { + return "error" + } + return "" + }() + } + + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_%v_atList", f.Name, p.Name), + Fullname: fmt.Sprintf("%v::__%v_%v_atList", c.Name, f.Name, p.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: output, + Parameters: params, + Signature: "()", + Container: strings.Split(p.Value, "<")[0], + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + PureGoOutput: func() string { + if strings.Contains(p.PureGoType, "]error") { + return "error" + } + return "" + }(), + IsMap: isMap, + }) + } + + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_%v_setList", f.Name, p.Name), f.OverloadNumber) { + var params = func() []*Parameter { + if IsPackedList(CleanValue(p.Value)) { + return []*Parameter{{Name: "i", Value: strings.Split(strings.Split(p.Value, "<")[1], ">")[0], + PureGoType: func() string { + if strings.Contains(p.PureGoType, "]error") { + return "error" + } + return "" + }()}} + } + var key, value = UnpackedMapDirty(CleanValue(p.Value)) + return []*Parameter{{Name: "key", Value: key, + PureGoType: func() string { + if strings.Contains(p.PureGoType, "[error]") { + return "error" + } + return "" + }()}, {Name: "i", Value: value, + PureGoType: func() string { + if strings.Contains(p.PureGoType, "]error") { + return "error" + } + return "" + }()}} + }() + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_%v_setList", f.Name, p.Name), + Fullname: fmt.Sprintf("%v::__%v_%v_setList", c.Name, f.Name, p.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "void", + Parameters: params, + Signature: "()", + Container: strings.Split(p.Value, "<")[0], + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + }) + } + + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_%v_newList", f.Name, p.Name), f.OverloadNumber) { + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_%v_newList", f.Name, p.Name), + Fullname: fmt.Sprintf("%v::__%v_%v_newList", c.Name, f.Name, p.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "void *", + Signature: "()", + Container: p.Value, + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + }) + } + + if IsPackedMap(CleanValue(p.Value)) { + if !c.HasFunctionWithNameAndOverloadNumber(fmt.Sprintf("__%v_%v_keyList", f.Name, p.Name), f.OverloadNumber) { + var keyType, _ = UnpackedMapDirty(CleanValue(p.Value)) + c.Functions = append(c.Functions, &Function{ + Name: fmt.Sprintf("__%v_%v_keyList", f.Name, p.Name), + Fullname: fmt.Sprintf("%v::__%v_%v_keyList", c.Name, f.Name, p.Name), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: fmt.Sprintf("QList<%v>", keyType), + Signature: "()", + OverloadNumber: f.OverloadNumber, + Overload: f.Overload, + NoMocDeduce: true, + PureGoOutput: func() string { + if strings.Contains(p.PureGoType, "[error]") { + return "[]error" + } + return "" + }(), + Container: CleanValue(p.Value), + }) + rec = true + } + } + } + } + } + if rec { + c.FixGenericHelper() + } + } +} diff --git a/qt/tool-chain/binding/parser/helper.go b/qt/tool-chain/binding/parser/helper.go new file mode 100644 index 0000000..173b27a --- /dev/null +++ b/qt/tool-chain/binding/parser/helper.go @@ -0,0 +1,596 @@ +package parser + +import ( + "bytes" + "fmt" + "runtime" + "sort" + "strings" + "sync" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +const ( + SIGNAL = "signal" + SLOT = "slot" + PROP = "prop" + + IMPURE = "impure" + PURE = "pure" + + PLAIN = "plain" + CONSTRUCTOR = "constructor" + COPY_CONSTRUCTOR = "copy-constructor" + MOVE_CONSTRUCTOR = "move-constructor" + DESTRUCTOR = "destructor" + + CONNECT = "Connect" + DISCONNECT = "Disconnect" + CALLBACK = "callback" + + GETTER = "getter" + SETTER = "setter" + + VOID = "void" + + TILDE = "~" + + MOC = "moc" +) + +func UseJs() bool { return State.Target == "js" || State.Target == "wasm" } +func UseWasm() bool { return State.Target == "wasm" } + +func IsPackedList(v string) bool { + return (strings.HasPrefix(v, "QList<") || + //TODO: QLinkedList + strings.HasPrefix(v, "QVector<") || + strings.HasPrefix(v, "QStack<") || + strings.HasPrefix(v, "QQueue<")) && + //TODO: QSet + + strings.Count(v, "<") == 1 //TODO: +} + +func UnpackedList(v string) string { + return CleanValue(UnpackedListDirty(v)) +} + +func UnpackedListDirty(v string) string { + return strings.Split(strings.Split(v, "<")[1], ">")[0] +} + +func IsPackedMap(v string) bool { + return (strings.HasPrefix(v, "QMap<") || + strings.HasPrefix(v, "QMultiMap<") || + strings.HasPrefix(v, "QHash<") || + strings.HasPrefix(v, "QMultiHash<")) && + + strings.Count(v, "<") == 1 //TODO: +} + +func UnpackedMap(v string) (string, string) { + var splitted = strings.Split(UnpackedList(v), ",") + return splitted[0], splitted[1] +} + +func UnpackedMapDirty(v string) (string, string) { + var splitted = strings.Split(UnpackedListDirty(v), ",") + return splitted[0], splitted[1] +} + +func UnpackedGoMapDirty(v string) []string { + if !strings.Contains(v, "]") { //TODO: multidimensional array and nested maps + return make([]string, 2) + } + return strings.Split(v, "]") +} + +func CleanValue(v string) string { + if IsPackedList(cleanValueUnsafe(v)) || IsPackedMap(cleanValueUnsafe(v)) { + var inside = strings.Split(strings.Split(v, "<")[1], ">")[0] + return strings.Replace(cleanValueUnsafe(v), strings.Split(strings.Split(cleanValueUnsafe(v), "<")[1], ">")[0], inside, -1) + } + v = cleanValueUnsafe(v) + if vC, ok := IsClass(v); ok { + v = vC + } + return v +} + +func IsClass(value string) (string, bool) { + if strings.Contains(value, ".") { + return IsClass(strings.Split(value, ".")[1]) + } + if strings.Contains(value, "::") { + return IsClass(strings.Split(value, "::")[1]) + } + var _, ok = State.ClassMap[value] + return value, ok +} + +func cleanValueUnsafe(v string) string { + for _, b := range []string{"*", "const", "&", "&", ";"} { + v = strings.Replace(v, b, "", -1) + } + return strings.TrimSpace(v) +} + +func CleanName(name, value string) string { + switch name { + case + "type", + "func", + "range", + "string", + "int", + "map", + "const", + "interface", + "select", + "strings", + "new", + "signal", + "ptr", + "register", + "forever", + "len", + "unsafe", + "log", + "runtime", + "time", + "hex", + "script": + { + return name[:len(name)-2] + } + + case "": + { + var v = strings.Replace(CleanValue(value), ".", "", -1) + if len(v) >= 3 { + return fmt.Sprintf("v%v", strings.ToLower(v[:2])) + } else { + return fmt.Sprintf("v%v", strings.ToLower(v)) + } + } + + case "f", "fmt", "qt", "js": + { + return name + name + } + } + + return name +} + +//TODO: remove global +var LibDepsMutex = new(sync.Mutex) +var LibDeps = map[string][]string{ + "Core": {"Widgets", "Gui", "Svg"}, //Widgets, Gui //Svg + "AndroidExtras": {"Core"}, + "Gui": {"Widgets", "Core"}, //Widgets + "Network": {"Core"}, + "Xml": {"XmlPatterns", "Core"}, //XmlPatterns + "DBus": {"Core"}, + "Nfc": {"Core"}, + "Script": {"Core"}, + "Sensors": {"Core"}, + "Positioning": {"Core"}, + "Widgets": {"Gui", "Core"}, + "Sql": {"Widgets", "Gui", "Core"}, //Widgets, Gui + "MacExtras": {"Gui", "Core"}, + "Qml": {"Network", "Core"}, + "WebSockets": {"Network", "Core"}, + "XmlPatterns": {"Network", "Core"}, + "Bluetooth": {"Core"}, + "WebChannel": {"Network", "Qml", "Core"}, //Network (needed for static linking ios) + "Svg": {"Widgets", "Gui", "Core"}, + "Multimedia": {"MultimediaWidgets", "Widgets", "Network", "Gui", "Core"}, //MultimediaWidgets, Widgets + "Quick": {"QuickWidgets", "Widgets", "Network", "Qml", "Gui", "Core"}, //QuickWidgets, Widgets, Network (needed for static linking ios) + "Help": {"Sql", "Network", "Widgets", "Gui", "Core"}, //Sql + CLucene + Network (needed for static linking ios) + "Location": {"Positioning", "Quick", "Gui", "Core"}, + "ScriptTools": {"Script", "Widgets", "Core"}, //Script, Widgets + "UiTools": {"Widgets", "Gui", "Core"}, + "X11Extras": {"Gui", "Core"}, + "WinExtras": {"Widgets", "Gui", "Core"}, + "WebEngine": {"Widgets", "WebEngineWidgets", "WebChannel", "Network", "WebEngineCore", "Quick", "PrintSupport", "Gui", "Qml", "Positioning", "Core"}, //Widgets, WebEngineWidgets, WebChannel, Network + "TestLib": {"Widgets", "Gui", "Core"}, //Widgets, Gui + "SerialPort": {"Core"}, + "SerialBus": {"Core"}, + "PrintSupport": {"Widgets", "Gui", "Core"}, + //"PlatformHeaders": []string{}, //TODO: uncomment + "Designer": {"UiPlugin", "Widgets", "Gui", "Xml", "Core"}, + "Scxml": {"Network", "Qml", "Core"}, //Network (needed for static linking ios) + "Gamepad": {"Gui", "Core"}, + + "Purchasing": {"Core"}, + "DataVisualization": {"Gui", "Core"}, + "Charts": {"Widgets", "Gui", "Core"}, + //"Quick2DRenderer": {}, //TODO: uncomment + + "Speech": {"Core"}, + "QuickControls2": {"Quick", "QuickWidgets", "Widgets", "Network", "Qml", "Gui", "Core"}, //Quick, QuickWidgets, Widgets, Network, Qml, Gui (needed for static linking ios) + + "Sailfish": {"Core"}, + "WebView": {"Core"}, + + "NetworkAuth": {"Network", "Gui", "Core"}, + "RemoteObjects": {"Network", "Core"}, + + "WebKit": {"WebKitWidgets", "Multimedia", "Positioning", "Widgets", "Sql", "Network", "Gui", "Sensors", "Core"}, + + MOC: make([]string, 0), + "build_static": {"Qml"}, //TODO: REVIEW "Core", "Gui"}, +} + +func ShouldBuildForTarget(module, target string) bool { + + switch target { + case "windows": + if runtime.GOOS == "windows" { + return true + } + switch module { + case "WebEngine", "Designer", "Speech", "WebView": + return false + } + if strings.HasSuffix(module, "Extras") && module != "WinExtras" { + return false + } + + case "android", "android-emulator": + switch module { + case "DBus", "WebEngine", "Designer", "SerialPort", "SerialBus", "PrintSupport": //TODO: PrintSupport + return false + } + if strings.HasSuffix(module, "Extras") && module != "AndroidExtras" { + return false + } + + case "ios", "ios-simulator": + switch module { + case "DBus", "WebEngine", "SerialPort", "SerialBus", "Designer", "PrintSupport": //TODO: PrintSupport + return false + } + if strings.HasSuffix(module, "Extras") { + return false + } + + case "sailfish", "sailfish-emulator", "asteroid": + { + if !IsWhiteListedSailfishLib(module) { + return false + } + } + + case "rpi1", "rpi2", "rpi3": + { + switch module { + case "WebEngine", "Designer": + return false + } + if strings.HasSuffix(module, "Extras") { + return false + } + if utils.QT_RPI() && !IsWhiteListedRaspberryLib(module) { + return false + } + } + + case "js", "wasm": + { + switch module { + case "DBus", "Designer", "Positioning", "Help", "Location", "UiTools", "WebEngine", "SerialPort", "SerialBus", "Sql": + return false + } + if strings.HasSuffix(module, "Extras") { + return false + } + if !IsWhiteListedJsLib(module) && module != "build_static" { + return false + } + } + } + + return true +} + +func IsWhiteListedSailfishLib(name string) bool { + switch name { + case "Sailfish", "Core", "Quick", "Qml", "Network", "Gui", "Concurrent", "Multimedia", "Sql", "Svg", "XmlPatterns", "Xml", "DBus", "WebKit", "Sensors", "Positioning": + return true + + default: + return false + } +} + +//TODO: whitelist everything once dependency issue is resolved +func IsWhiteListedJsLib(name string) bool { + switch name { + case "Core", "Gui", "Widgets", "PrintSupport", "Qml", "Quick", "QuickControls2", "Xml", "XmlPatterns", "WebSockets", "Svg", "Charts", "Multimedia": + return true + + default: + return false + } +} + +func IsWhiteListedRaspberryLib(name string) bool { + switch name { + case "Core", "Gui", "Widgets", "PrintSupport", "Sql", "Qml", "Quick", "QuickControls2", "Svg", "SerialPort": + return true + + default: + return false + } +} + +func GetLibs() []string { + libs := []string{ + "Core", + "AndroidExtras", + "Gui", + "Network", + "Xml", + "DBus", + "Nfc", + "Script", //depreached (planned) in 5.6 + "Sensors", + "Positioning", + "Widgets", + "Sql", + "MacExtras", + "Qml", + "WebSockets", + "XmlPatterns", + "Bluetooth", + "WebChannel", + "Svg", + "Multimedia", + "Quick", + "Help", + "Location", + "ScriptTools", //depreached (planned) in 5.6 + "UiTools", + //"X11Extras", //TODO: + "WinExtras", + "WebEngine", + "TestLib", + "SerialPort", + "SerialBus", + "PrintSupport", + //"PlatformHeaders", //missing imports/guards + "Designer", + "Scxml", + "Gamepad", + + "Purchasing", + "DataVisualization", //GPLv3 + "Charts", //GPLv3 + //"Quick2DRenderer", //GPLv3 + //"VirtualKeyboard", //GPLv3 + + "Speech", + "QuickControls2", + + "Sailfish", + "WebView", + + //"NetworkAuth", //TODO: + "RemoteObjects", + + "WebKit", + } + + for i := len(libs) - 1; i >= 0; i-- { + switch { + case !(runtime.GOOS == "darwin" || runtime.GOOS == "linux") && (libs[i] == "WebEngine" || libs[i] == "WebView"), + runtime.GOOS != "windows" && libs[i] == "WinExtras", + runtime.GOOS != "darwin" && libs[i] == "MacExtras", + runtime.GOOS != "linux" && libs[i] == "X11Extras": + libs = append(libs[:i], libs[i+1:]...) + + case utils.QT_VERSION_NUM() < 5080 && libs[i] == "Speech": + libs = append(libs[:i], libs[i+1:]...) + + case (utils.QT_VERSION_NUM() < 5090 || utils.QT_MXE()) && (libs[i] == "NetworkAuth" || libs[i] == "RemoteObjects"): + libs = append(libs[:i], libs[i+1:]...) + + case !utils.QT_WEBKIT() && libs[i] == "WebKit": + libs = append(libs[:i], libs[i+1:]...) + + case (utils.QT_MSYS2() || utils.QT_PKG_CONFIG()) && libs[i] == "Purchasing": + libs = append(libs[:i], libs[i+1:]...) + } + } + return libs +} + +var ( + getCustomLibsCache = make(map[string]string) + getCustomLibsCacheMutex = new(sync.Mutex) +) + +func GetCustomLibs(target, tags string) map[string]string { + + /*TODO: cycle dep of cmd.BuildEnv + env, tags, _, _ := cmd.BuildEnv(target, "", "") + if tagsCustom != "" { + tags = append(tags, strings.Split(tagsCustom, " ")...) + } + */ + + wg := new(sync.WaitGroup) + wc := make(chan bool, 50) + out := make(map[string]string) + outMutex := new(sync.Mutex) + + lookup := func(lm map[string]*Class) { + for _, c := range lm { + if c.Pkg == "" { + continue + } + + wg.Add(1) + wc <- true + go func(c *Class) { + getCustomLibsCacheMutex.Lock() + path, ok := getCustomLibsCache[c.Pkg] + getCustomLibsCacheMutex.Unlock() + + if !ok { + cmd := utils.GoList("{{.ImportPath}}", fmt.Sprintf("-tags=\"%v\"", tags)) + cmd.Dir = c.Pkg + + /*TODO: cycle dep of cmd.BuildEnv + for k, v := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", k, v)) + } + */ + + path = strings.TrimSpace(utils.RunCmd(cmd, "get import path")) + getCustomLibsCacheMutex.Lock() + getCustomLibsCache[c.Pkg] = path + getCustomLibsCacheMutex.Unlock() + } + + outMutex.Lock() + out[c.Module] = path + outMutex.Unlock() + + <-wc + wg.Done() + }(c) + } + } + + lookup(State.ClassMap) + lookup(State.GoClassMap) + + wg.Wait() + + return out +} + +func Dump() { + for _, c := range State.ClassMap { + var bb = new(bytes.Buffer) + defer bb.Reset() + + fmt.Fprint(bb, "funcs\n\n") + for _, f := range c.Functions { + fmt.Fprintln(bb, f) + } + + fmt.Fprint(bb, "\n\nenums\n\n") + for _, e := range c.Enums { + fmt.Fprintln(bb, e) + } + + utils.MkdirAll(utils.GoQtPkgPath("tool-chain", "binding", "dump", c.Module)) + utils.SaveBytes(utils.GoQtPkgPath("tool-chain", "binding", "dump", c.Module, fmt.Sprintf("%v.txt", c.Name)), bb.Bytes()) + } +} + +func SortedClassNamesForModule(module string, template bool) []string { + var output = make([]string, 0) + for _, class := range State.ClassMap { + for _, pm := range strings.Split(module, ",") { + if class.Module == pm { + output = append(output, class.Name) + } + } + } + sort.Stable(sort.StringSlice(output)) + + if (module == MOC || strings.HasPrefix(module, "custom_")) && template { + items := make(map[string]string) + for _, cn := range output { + if class, ok := State.ClassMap[cn]; ok { + items[cn] = class.Bases + } + } + + tmpOutput := make([]string, 0) + + for item, dep := range items { + depClass, ok := State.ClassMap[dep] + if !ok { + delete(items, item) + continue + } + + //filter out everything that has no moc dep + if !(depClass.Module == MOC || strings.HasPrefix(depClass.Module, "custom_")) { + tmpOutput = append(tmpOutput, item) + delete(items, item) + continue + } + } + + for len(items) > 0 { + for item, dep := range items { + + depClass, ok := State.ClassMap[dep] + if !ok { + delete(items, item) + continue + } + + //filter out everything that has the fewest dependencies + if hasFewestDeps(items, depClass.Name) { + tmpOutput = append(tmpOutput, item) + delete(items, item) + continue + } + + //filter out everything that has resolved dep + for _, key := range tmpOutput { + if key == dep { + tmpOutput = append(tmpOutput, item) + delete(items, item) + break + } + } + + } + } + output = tmpOutput //TODO: make order deterministic + } + + return output +} + +func hasFewestDeps(i map[string]string, check string) bool { + dif := 100 + var base string + for _, v := range i { + c, ok := State.ClassMap[v] + if !ok { + continue + } + if ndif := len(c.GetAllBases()); ndif < dif { + dif = ndif + base = v + } + } + return base == check +} + +func SortedClassesForModule(module string, template bool) []*Class { + var ( + classNames = SortedClassNamesForModule(module, template) + output = make([]*Class, len(classNames)) + ) + for i, name := range classNames { + output[i] = State.ClassMap[name] + } + return output +} + +func IsBlackListedPureGoType(s string) bool { + return strings.Contains(s, "error") && !strings.HasSuffix(s, "][]error") +} diff --git a/qt/tool-chain/binding/parser/module.go b/qt/tool-chain/binding/parser/module.go new file mode 100644 index 0000000..cf17e55 --- /dev/null +++ b/qt/tool-chain/binding/parser/module.go @@ -0,0 +1,106 @@ +package parser + +import ( + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +type Module struct { + Namespace *Namespace `xml:"namespace"` + Project string `xml:"project,attr"` + Pkg string +} + +type Namespace struct { + Classes []*Class `xml:"class"` + //Functions []*Function `xml:"function"` //TODO: uncomment + //Enums []*Enum `xml:"enum"` //TODO: uncomment + SubNamespaces []*SubNamespace `xml:"namespace"` +} + +type SubNamespace struct { + //Classes []*Class `xml:"class"` //TODO: uncomment + Functions []*Function `xml:"function"` + Enums []*Enum `xml:"enum"` + Status string `xml:"status,attr"` + Access string `xml:"access,attr"` +} + +func (m *Module) Prepare() error { + if utils.QT_API_NUM(utils.QT_VERSION()) >= 5120 && m.Project == "QtQuickControls" { + m.Project = "QtQuickControls2" + } + + utils.Log.WithField("module", strings.TrimPrefix(m.Project, "Qt")).Debug("prepare") + + //register classes from namespace + for _, c := range m.Namespace.Classes { + c.register(m) + } + m.add() + + //register enums and functions from subnamespaces + var snsExtraClasses []*Class + for _, sns := range m.Namespace.SubNamespaces { + for _, e := range sns.Enums { + if !(e.Status == "active" || e.Status == "commendable") || !(e.Access == "public" || e.Access == "protected") || + strings.Contains(e.Fullname, "Private") || strings.Contains(e.Fullname, "Util") || + strings.Contains(e.Fullname, "nternal") || strings.ToLower(e.Name) == e.Name { + continue + } + e.register(m.Project) + } + if m.Project != "QtSensors" && m.Project != "QtXmlPatterns" && + m.Project != "QtQml" && m.Project != "QtWidgets" && m.Project != "QtMacExtras" && + m.Project != "QtTestLib" && m.Project != "QtScript" && m.Project != "QtQuick" { + for _, f := range sns.Functions { + + if !(f.Status == "active" || f.Status == "commendable") || !(f.Access == "public" || f.Access == "protected") || + strings.Contains(f.Fullname, "Private") || strings.Contains(f.Fullname, "Util") || + strings.Contains(f.Fullname, "nternal") || f.Name == "qDefaultSurfaceFormat" || + f.ClassName() == "QUnicodeTables" || + f.ClassName() == "QUtf8Functions" || + f.ClassName() == "QUnicodeTools" || + f.ClassName() == "HPack" || + f.ClassName() == "QHighDpi" || + f.ClassName() == "QPdf" || + f.ClassName() == "QPlatformGraphicsBufferHelper" || + f.ClassName() == "QIcu" || + strings.ToLower(f.ClassName()) == f.ClassName() { + continue + } + + if m.Project == "QtWebEngine" && f.Name != "initialize" { + continue + } + + f.Static = true + f.register(m.Project) + if c, ok := f.Class(); ok { + if l := len(snsExtraClasses); l > 0 && snsExtraClasses[l-1].Name == c.Name { + continue + } + snsExtraClasses = append(snsExtraClasses, c) + } + } + } + } + + //mutate classmap + m.remove() + + //mutate classes + for _, c := range append(SortedClassesForModule(m.Project, false), snsExtraClasses...) { + c.add() + c.fix() + c.remove() + } + + //register derivations + for _, c := range m.Namespace.Classes { + c.derivation() + } + + return nil +} diff --git a/qt/tool-chain/binding/parser/module_add.go b/qt/tool-chain/binding/parser/module_add.go new file mode 100644 index 0000000..d46a40a --- /dev/null +++ b/qt/tool-chain/binding/parser/module_add.go @@ -0,0 +1,84 @@ +package parser + +import "fmt" + +func (m *Module) add() { + switch m.Project { + case "QtWebEngine": + if _, ok := State.ClassMap["QWebEngineCertificateError"]; ok { + return + } + c := &Class{ + Name: "QWebEngineCertificateError", + Status: "active", + Module: m.Project, + Access: "public", + Functions: []*Function{ + { + Name: "error", + Fullname: fmt.Sprintf("%v::%v", "QWebEngineCertificateError", "error"), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "Error", + Parameters: []*Parameter{}, + Signature: "Error error() const", + }, + { + Name: "errorDescription", + Fullname: fmt.Sprintf("%v::%v", "QWebEngineCertificateError", "errorDescription"), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "QString", + Parameters: []*Parameter{}, + Signature: "QString errorDescription() const", + }, + { + Name: "isOverridable", + Fullname: fmt.Sprintf("%v::%v", "QWebEngineCertificateError", "isOverridable"), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "bool", + Parameters: []*Parameter{}, + Signature: "bool isOverridable() const", + }, + { + Name: "url", + Fullname: fmt.Sprintf("%v::%v", "QWebEngineCertificateError", "url"), + Access: "public", + Virtual: "non", + Meta: PLAIN, + Output: "QUrl", + Parameters: []*Parameter{}, + Signature: "QUrl url() const", + }, + }, + Enums: []*Enum{{ + Name: "Error", + Fullname: "QWebEngineCertificateError::Error", + Status: "active", + Access: "public", + Values: []*Value{ + {"SslPinnedKeyNotInCertificateChain", "-150"}, + {"CertificateCommonNameInvalid", "-200"}, + {"CertificateDateInvalid", "-201"}, + {"CertificateAuthorityInvalid", "-202"}, + {"CertificateContainsErrors", "-203"}, + {"CertificateNoRevocationMechanism", "-204"}, + {"CertificateUnableToCheckRevocation", "-205"}, + {"CertificateRevoked", "-206"}, + {"CertificateInvalid", "-207"}, + {"CertificateWeakSignatureAlgorithm", "-208"}, + {"CertificateNonUniqueName", "-210"}, + {"CertificateWeakKey", "-211"}, + {"CertificateNameConstraintViolation", "-212"}, + {"CertificateValidityTooLong", "-213"}, + {"CertificateTransparencyRequired", "-214"}, + }}, + }, + } + c.register(m) + } +} diff --git a/qt/tool-chain/binding/parser/module_remove.go b/qt/tool-chain/binding/parser/module_remove.go new file mode 100644 index 0000000..4111988 --- /dev/null +++ b/qt/tool-chain/binding/parser/module_remove.go @@ -0,0 +1,16 @@ +package parser + +func (m *Module) remove() { + m.removeClasses() +} + +func (m *Module) removeClasses() { + for _, c := range SortedClassesForModule(m.Project, false) { + switch { + case + !(c.Access == "public" || c.Access == "protected"), + c.Name == "qoutputrange": + delete(State.ClassMap, c.Name) + } + } +} diff --git a/qt/tool-chain/binding/parser/parser.go b/qt/tool-chain/binding/parser/parser.go new file mode 100644 index 0000000..7a9b328 --- /dev/null +++ b/qt/tool-chain/binding/parser/parser.go @@ -0,0 +1,123 @@ +package parser + +import ( + "encoding/xml" + "fmt" + "path/filepath" + "strings" + "sync" + + "github.com/peterq/pan-light/qt/tool-chain/utils" + "github.com/sirupsen/logrus" +) + +var State = &struct { + ClassMap map[string]*Class + GoClassMap map[string]*Class + + MocImports map[string]struct{} + Minimal bool //TODO: + Target string +}{ + ClassMap: make(map[string]*Class), + GoClassMap: make(map[string]*Class), + MocImports: make(map[string]struct{}), +} + +func LoadModules() { + libs := GetLibs() + modules := make([]*Module, len(libs)) + modulesMutex := new(sync.Mutex) + wg := new(sync.WaitGroup) + + wg.Add(len(GetLibs())) + for i, m := range libs { + go func(i int, m string) { + mod := LoadModule(m) + + modulesMutex.Lock() + modules[i] = mod + modulesMutex.Unlock() + wg.Done() + }(i, m) + } + wg.Wait() + + for _, m := range modules { + if m != nil { + m.Prepare() + } + } +} + +func LoadModule(m string) *Module { + var ( + logName = "parser.LoadModule" + logFields = logrus.Fields{"module": m} + ) + utils.Log.WithFields(logFields).Debug(logName) + + if m == "Sailfish" { + return sailfishModule() + } + + if utils.QT_API_NUM(utils.QT_VERSION()) >= 5120 && m == "QuickControls2" { + m = "QuickControls" + } + + module := new(Module) + var err error + switch { + case utils.QT_WEBKIT() && m == "WebKit": + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/5.8.0"), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_MXE(): + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.12.0")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_HOMEBREW(): + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.12.0")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_MACPORTS(), utils.QT_NIX(): + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.11.1")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_MSYS2(): + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.12.0")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_UBPORTS_VERSION() == "xenial": + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.9.0")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_SAILFISH(): + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.6.3")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_RPI(): + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API("5.7.0")), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + + case utils.QT_PKG_CONFIG(): + if utils.QT_API("") != "" { + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API(utils.QT_VERSION())), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + } else { + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(utils.QT_DOC_DIR(), fmt.Sprintf("qt%v", strings.ToLower(m)), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + } + + default: + if utils.QT_API("") != "" { + err = xml.Unmarshal([]byte(utils.LoadOptional(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/binding/files/docs/"+utils.QT_API(utils.QT_VERSION())), "get doc dir")), fmt.Sprintf("qt%v.index", strings.ToLower(m))))), &module) + } else { + path := filepath.Join(utils.QT_DIR(), "Docs", fmt.Sprintf("Qt-%v", utils.QT_VERSION_MAJOR()), fmt.Sprintf("qt%v", strings.ToLower(m)), fmt.Sprintf("qt%v.index", strings.ToLower(m))) + if !utils.ExistsDir(filepath.Join(utils.QT_DIR(), "Docs", fmt.Sprintf("Qt-%v", utils.QT_VERSION_MAJOR()))) { + path = filepath.Join(utils.QT_DIR(), "Docs", fmt.Sprintf("Qt-%v", utils.QT_VERSION()), fmt.Sprintf("qt%v", strings.ToLower(m)), fmt.Sprintf("qt%v.index", strings.ToLower(m))) + } + err = xml.Unmarshal([]byte(utils.Load(path)), &module) + } + } + if err != nil { + if m != "DataVisualization" && m != "Charts" { + utils.Log.WithFields(logFields).WithError(err).Warn(logName) + } else { + utils.Log.WithFields(logFields).WithError(err).Debug(logName) + } + return nil + } + + return module +} diff --git a/qt/tool-chain/binding/parser/sailfish.go b/qt/tool-chain/binding/parser/sailfish.go new file mode 100644 index 0000000..116eac6 --- /dev/null +++ b/qt/tool-chain/binding/parser/sailfish.go @@ -0,0 +1,94 @@ +package parser + +func sailfishModule() *Module { + return &Module{ + Project: "QtSailfish", + Namespace: &Namespace{ + Classes: []*Class{ + { + Name: "SailfishApp", + Access: "public", + Module: "QtSailfish", + Functions: []*Function{ //TODO: should be in Namespace.Functions + { + Name: "application", + Fullname: "SailfishApp::application", + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "QGuiApplication*", + Signature: "()", + Parameters: []*Parameter{ + { + Name: "argc", + Value: "int &", + }, + { + Name: "argv", + Value: "char **", + }, + }, + }, + { + Name: "main", + Fullname: "SailfishApp::main", + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "int", + Signature: "()", + Parameters: []*Parameter{ + { + Name: "argc", + Value: "int &", + }, + { + Name: "argv", + Value: "char **", + }, + }, + }, + { + Name: "createView", + Fullname: "SailfishApp::createView", + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "QQuickView*", + Signature: "()", + }, + { + Name: "pathTo", + Fullname: "SailfishApp::pathTo", + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "QUrl", + Signature: "pathTo(const QString &filename)", + Parameters: []*Parameter{ + { + Name: "filename", + Value: "QString &", + }, + }, + }, + { + Name: "pathToMainQml", + Fullname: "SailfishApp::pathToMainQml", + Access: "public", + Virtual: "non", + Meta: PLAIN, + Static: true, + Output: "QUrl", + Signature: "pathToMainQml()", + }, + }, + }, + }, + }, + } +} diff --git a/qt/tool-chain/binding/parser/variable.go b/qt/tool-chain/binding/parser/variable.go new file mode 100644 index 0000000..62c8b70 --- /dev/null +++ b/qt/tool-chain/binding/parser/variable.go @@ -0,0 +1,193 @@ +package parser + +import ( + "fmt" + "strings" +) + +type Variable struct { + Name string `xml:"name,attr"` + Fullname string `xml:"fullname,attr"` + Href string `xml:"href,attr"` + Status string `xml:"status,attr"` + Access string `xml:"access,attr"` + Filepath string `xml:"filepath,attr"` + Static bool `xml:"static,attr"` + Output string `xml:"type,attr"` + Brief string `xml:"brief,attr"` + Getter []struct{} `xml:"getter"` + Setter []struct{} `xml:"setter"` + + IsMocSynthetic bool + PureGoType string + Connect int + ConnectGet bool + ConnectSet bool + ConnectChanged bool + Target string + Inbound bool +} + +func (v *Variable) Class() (*Class, bool) { + var class, ok = State.ClassMap[v.ClassName()] + return class, ok +} + +func (v *Variable) ClassName() string { + var s = strings.Split(v.Fullname, "::") + if len(s) == 3 { + return s[1] + } + return s[0] +} + +func (v *Variable) varToFunc() []*Function { + var funcs = make([]*Function, 0) + + var class, ok = v.Class() + if !ok || class.HasFunctionWithName(v.Name) { + return funcs + } + + funcs = append(funcs, &Function{ + Name: v.Name, + Fullname: v.Fullname, + Href: v.Href, + Status: v.Status, + Access: v.Access, + Filepath: v.Filepath, + Static: v.Static, + Output: v.Output, + Meta: GETTER, + Brief: v.Brief, + }) + + if strings.Contains(v.Output, "const") { + return funcs + } + + funcs = append(funcs, &Function{ + Name: fmt.Sprintf("set%v", strings.Title(v.Name)), + Fullname: fmt.Sprintf("%v::set%v", v.ClassName(), strings.Title(v.Name)), + Href: v.Href, + Status: v.Status, + Access: v.Access, + Filepath: v.Filepath, + Static: v.Static, + Output: "void", + Meta: SETTER, + Parameters: []*Parameter{{Value: v.Output}}, + TmpName: v.Name, + Brief: v.Brief, + }) + + return funcs +} + +func (v *Variable) propToFunc(c *Class) []*Function { + var funcs = make([]*Function, 0) + + if len(v.Getter) != 0 { + return funcs + } + + if !(c.HasFunctionWithName(v.Name) || + c.HasFunctionWithName(fmt.Sprintf("is%v", strings.Title(v.Name))) || + c.HasFunctionWithName(fmt.Sprintf("has%v", strings.Title(v.Name)))) { + + tmpF := &Function{ + Name: v.Name, + Fullname: v.Fullname, + Href: v.Href, + Status: v.Status, + Access: v.Access, + Filepath: v.Filepath, + Static: v.Static, + Output: v.Output, + Meta: PLAIN, + Virtual: func() string { + if c.Module == MOC && !v.IsMocSynthetic { + return IMPURE + } + return "" + }(), + Signature: "()", + IsMocFunction: c.Module == MOC, + IsMocProperty: c.Module == MOC, + } + + if tmpF.Output == "bool" { + if !strings.HasPrefix(strings.ToLower(v.Name), "is") { + tmpF.Name = fmt.Sprintf("is%v", strings.Title(tmpF.Name)) + } + tmpF.Fullname = fmt.Sprintf("%v::%v", tmpF.ClassName(), tmpF.Name) + } + + funcs = append(funcs, tmpF) + } + + if len(v.Setter) != 0 || c.HasFunctionWithName(fmt.Sprintf("set%v", strings.Title(v.Name))) { + return funcs + } + + funcs = append(funcs, &Function{ + Name: fmt.Sprintf("set%v", strings.Title(v.Name)), + Fullname: fmt.Sprintf("%v::set%v", v.ClassName(), strings.Title(v.Name)), + Href: v.Href, + Status: v.Status, + Access: v.Access, + Filepath: v.Filepath, + Static: v.Static, + Output: "void", + Meta: PLAIN, + Virtual: func() string { + if c.Module == MOC && !v.IsMocSynthetic { + return IMPURE + } + return "" + }(), + Parameters: []*Parameter{{Name: v.Name, Value: v.Output}}, + Signature: "()", + IsMocFunction: c.Module == MOC, + IsMocProperty: c.Module == MOC, + }) + + if c.Module == MOC { + funcs = append(funcs, &Function{ + Name: fmt.Sprintf("%vChanged", v.Name), + Fullname: fmt.Sprintf("%v::%vChanged", v.ClassName(), v.Name), + Status: v.Status, + Access: v.Access, + Output: "void", + Meta: SIGNAL, + Parameters: []*Parameter{{Name: v.Name, Value: v.Output}}, + Signature: "()", + IsMocFunction: true, + }) + } + + //add all overloaded property functions from base classes + //TODO: move rest into seperate function, as this func is called multiple times + + for _, bc := range c.GetAllBases() { + var bclass, ok = State.ClassMap[bc] + if !ok { + continue + } + + for _, bcf := range bclass.Functions { + if bcf.Name != fmt.Sprintf("set%v", strings.Title(v.Name)) || !bcf.Overload { + continue + } + + var tmpF = *bcf + + tmpF.Name = fmt.Sprintf("set%v", strings.Title(v.Name)) + tmpF.Fullname = fmt.Sprintf("%v::%v", v.ClassName(), tmpF.Name) + + funcs = append(funcs, &tmpF) + } + } + + return funcs +} diff --git a/qt/tool-chain/binding/templater/enum_cpp.go b/qt/tool-chain/binding/templater/enum_cpp.go new file mode 100644 index 0000000..b5e4b47 --- /dev/null +++ b/qt/tool-chain/binding/templater/enum_cpp.go @@ -0,0 +1,46 @@ +package templater + +import ( + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func cppEnum(e *parser.Enum, v *parser.Value) string { + o := fmt.Sprintf("%v\n{\n\t%v\n}", cppEnumHeader(e, v), cppEnumBody(e, v)) + if UseJs() { + o = "EMSCRIPTEN_KEEPALIVE\n" + o + } + return o +} + +func cppEnumHeader(enum *parser.Enum, value *parser.Value) string { + return fmt.Sprintf("int %v_%v_Type()", enum.ClassName(), value.Name) +} + +func cppEnumBody(enum *parser.Enum, value *parser.Value) string { + //TODO: check for "since" tag in enums + + //needed for sailfish with 5.6 docs + if strings.HasPrefix(value.Name, "MV_") || strings.HasPrefix(value.Name, "PM_") || + strings.HasPrefix(value.Name, "SH_") || strings.HasPrefix(value.Name, "ISODate") || + strings.HasPrefix(value.Name, "TlsV1_") { + return fmt.Sprintf(`#if QT_VERSION >= 0x056000 + return %v::%v; + #else + return 0; + #endif`, enum.ClassName(), value.Name) + } + + //needed for msys2 with 5.7 docs + if strings.HasPrefix(value.Name, "PE_") || strings.HasPrefix(value.Name, "SE_") { + return fmt.Sprintf(`#if QT_VERSION >= 0x057000 + return %v::%v; + #else + return 0; + #endif`, enum.ClassName(), value.Name) + } + + return fmt.Sprintf("return %v::%v;", enum.ClassName(), value.Name) +} diff --git a/qt/tool-chain/binding/templater/enum_go.go b/qt/tool-chain/binding/templater/enum_go.go new file mode 100644 index 0000000..2b200a8 --- /dev/null +++ b/qt/tool-chain/binding/templater/enum_go.go @@ -0,0 +1,53 @@ +package templater + +import ( + "bytes" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func goEnum(e *parser.Enum, _ *parser.Value) string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + var t string + + fmt.Fprintf(bb, "//go:generate stringer -type=%v\n//%v\ntype %v int64\nconst (\n", strings.Replace(e.Fullname, ":", "_", -1), e.Fullname, strings.Replace(e.Fullname, ":", "_", -1)) + + for _, v := range e.Values { + switch v.Name { + case "ByteOrder": + { + + } + + default: + { + if strings.Contains(v.Value, " | ") { + var tArray = make([]string, 0) + for _, s := range strings.Split(v.Value, " | ") { + tArray = append(tArray, converter.GoEnum(v.Name, s, e)) + } + t = strings.Join(tArray, " | ") + } else { + t = converter.GoEnum(v.Name, v.Value, e) + } + var c, _ = e.Class() + if strings.HasPrefix(t, "C.") && c.Stub { + t = "0" + } + fmt.Fprintf(bb, "%v__%v %v = %v(%v)\n", strings.Split(e.Fullname, "::")[0], v.Name, strings.Replace(e.Fullname, ":", "_", -1), strings.Replace(e.Fullname, ":", "_", -1), t) + } + } + } + + fmt.Fprint(bb, ")") + + if e.NoConst || strings.Contains(e.Name, "Style") { + return strings.Replace(bb.String(), "const (", "var (", -1) + } + return bb.String() +} diff --git a/qt/tool-chain/binding/templater/function_cpp.go b/qt/tool-chain/binding/templater/function_cpp.go new file mode 100644 index 0000000..55c9443 --- /dev/null +++ b/qt/tool-chain/binding/templater/function_cpp.go @@ -0,0 +1,691 @@ +package templater + +import ( + "bytes" + "crypto/sha1" + "encoding/hex" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func cppFunctionCallback(function *parser.Function) string { + var output = fmt.Sprintf("%v { %v };", cppFunctionCallbackHeader(function), cppFunctionCallbackBody(function)) + if function.IsSupported() { + return cppFunctionCallbackWithGuards(function, output) + } + return "" +} + +func cppFunctionCallbackWithGuards(function *parser.Function, output string) string { + switch { + case + function.Fullname == "QProcess::nativeArguments", function.Fullname == "QProcess::setNativeArguments", + function.Fullname == "QAbstractEventDispatcher::registerEventNotifier", function.Fullname == "QAbstractEventDispatcher::unregisterEventNotifier": + { + return fmt.Sprintf("#ifdef Q_OS_WIN\n\t\t%v\n\t#endif", output) + } + + case + function.Fullname == "QSensorGesture::detected": + { + return fmt.Sprintf("#ifdef Q_QDOC\n\t\t%v\n\t#endif", output) + } + } + + return output +} + +func cppFunctionCallbackHeader(function *parser.Function) string { + return fmt.Sprintf("%v %v%v(%v)%v", + + func() string { + var c, _ = function.Class() + if parser.IsPackedMap(function.Output) && c.Module == parser.MOC && function.IsMocFunction { + var tHash = sha1.New() + tHash.Write([]byte(function.Output)) + return fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])) + } + return function.Output + }(), + + func() string { + if function.Meta == parser.SIGNAL { + return fmt.Sprintf("Signal_%v", strings.Title(function.Name)) + } + var c, _ = function.Class() + if strings.HasPrefix(function.Name, parser.TILDE) && c.Module != parser.MOC { + return strings.Replace(function.Name, parser.TILDE, fmt.Sprintf("%vMy", parser.TILDE), -1) + } + return function.Name + }(), + + func() string { + if function.Meta == parser.SIGNAL { + return function.OverloadNumber + } + return "" + }(), + + converter.CppInputParametersForCallbackHeader(function), + + func() string { + if strings.Contains(function.Signature, ") const") { + return " const" + } + return "" + }(), + ) +} + +func cppFunctionCallbackBody(function *parser.Function) string { + out := fmt.Sprintf("%v%v%v;", + + converter.CppInputParametersForCallbackBodyPrePack(function), + + func() string { + if converter.CppHeaderOutput(function) != parser.VOID { + return "return " + } + return "" + }(), + + func() string { + var output string + if UseJs() { + output = fmt.Sprintf("emscripten::val::global(\"Module\").call<%v>(\"_callback%v_%v%v\", %v)", converter.CppOutputTemplateJS(function), function.ClassName(), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.CppInputParametersForCallbackBody(function)) + } else { + output = fmt.Sprintf("callback%v_%v%v(%v)", function.ClassName(), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.CppInputParametersForCallbackBody(function)) + } + if converter.CppHeaderOutput(function) != parser.VOID { + output = converter.CppInput(output, function.Output, function) + } + if UseJs() { + output = strings.Replace(output, "static_cast", "reinterpret_cast", -1) + output = strings.Replace(output, "enum_cast", "static_cast", -1) + } + return output + }(), + ) + return out +} + +func cppFunction(function *parser.Function) string { + var output = fmt.Sprintf("%v\n{\n%v\n}", cppFunctionHeader(function), cppFunctionUnused(function, cppFunctionBodyWithGuards(function))) + if UseJs() { + if !strings.Contains(output, "_Packed") && !strings.Contains(output, "emscripten::val") { + output = strings.Replace(output, converter.CppHeaderName(function), "_KEEPALIVE_"+converter.CppHeaderName(function), -1) + output = "EMSCRIPTEN_KEEPALIVE\n" + output + } else { + output = strings.Replace(output, "static_cast", "reinterpret_cast", -1) + exportedFunctions = append(exportedFunctions, converter.CppHeaderName(function)) + } + output = strings.Replace(output, "enum_cast", "static_cast", -1) + } + if function.IsSupported() { + return output + } + return "" +} + +func cppFunctionHeader(function *parser.Function) string { + var output = fmt.Sprintf("%v %v(%v)", converter.CppHeaderOutput(function), converter.CppHeaderName(function), converter.CppHeaderInput(function)) + if UseJs() { + if strings.Contains(output, "_Packed") || strings.Contains(output, "emscripten::val") { + output = strings.Replace(output, "void*", "uintptr_t", -1) + function.BoundByEmscripten = true + } + } + if function.IsSupported() { + return output + } + return "" +} + +//TODO: +func cppFunctionUnused(function *parser.Function, body string) string { + + var tmp = make([]string, 0) + if !(function.Static || function.Meta == parser.CONSTRUCTOR) { + tmp = append(tmp, "ptr") + } + if function.Meta != parser.SIGNAL { + for _, p := range function.Parameters { + tmp = append(tmp, parser.CleanName(p.Name, p.Value)) + } + } + + bb := new(bytes.Buffer) + defer bb.Reset() + for _, p := range tmp { + if !strings.Contains(body, p) { + fmt.Fprintf(bb, "\tQ_UNUSED(%v);\n", p) + } + } + bb.WriteString(body) + return bb.String() +} + +func cppFunctionBodyWithGuards(function *parser.Function) string { + + if function.Default { + switch { + case + strings.HasPrefix(function.ClassName(), "QMac") && !strings.HasPrefix(parser.State.ClassMap[function.ClassName()].Module, "QtMac"): + { + return fmt.Sprintf("#ifdef Q_OS_OSX\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + } + } else { + switch { + case + function.Fullname == "QMenu::setAsDockMenu", function.Fullname == "QSysInfo::macVersion", function.Fullname == "QSysInfo::MacintoshVersion": + { + return fmt.Sprintf("#ifdef Q_OS_OSX\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + + case + function.Fullname == "QProcess::nativeArguments", function.Fullname == "QProcess::setNativeArguments", + function.Fullname == "QAbstractEventDispatcher::registerEventNotifier", function.Fullname == "QAbstractEventDispatcher::unregisterEventNotifier", + function.Fullname == "QSysInfo::windowsVersion": + { + return fmt.Sprintf("#ifdef Q_OS_WIN\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + + case + function.Fullname == "QScreen::model": + { + return fmt.Sprintf("#ifndef Q_OS_WIN\n%v\n#endif", cppFunctionBody(function)) + } + + case + function.Fullname == "QApplication::navigationMode", function.Fullname == "QApplication::setNavigationMode", + function.Fullname == "QWidget::hasEditFocus", function.Fullname == "QWidget::setEditFocus": + { + return fmt.Sprintf("#ifdef QT_KEYPAD_NAVIGATION\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + + case + function.Fullname == "QMenuBar::defaultAction", function.Fullname == "QMenuBar::setDefaultAction": + { + return fmt.Sprintf("#ifdef Q_OS_WINCE\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + + case + function.Fullname == "QWidget::setupUi", function.Fullname == "QSensorGesture::detected": + { + return fmt.Sprintf("#ifdef Q_QDOC\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + + case + function.Fullname == "QTextDocument::print", function.Fullname == "QPlainTextEdit::print", function.Fullname == "QTextEdit::print": + { + return fmt.Sprintf("#ifndef Q_OS_IOS\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + + case function.Name == "qmlRegisterType" && function.TemplateModeGo != "": + { + return fmt.Sprintf("#ifdef QT_QML_LIB\n%v%v\n#endif", cppFunctionBody(function), cppFunctionBodyFailed(function)) + } + } + } + + return cppFunctionBody(function) +} + +func cppFunctionBodyFailed(function *parser.Function) string { + if converter.CppHeaderOutput(function) != parser.VOID { + return fmt.Sprintf("\n#else\n\treturn %v;", converter.CppOutputParametersFailed(function)) + } + return "" +} + +func cppFunctionBody(function *parser.Function) string { + + var fakeDefault bool + if UseJs() && function.Meta == parser.SLOT { + defer func() { function.Meta = parser.SLOT; function.Default = false }() + function.Meta = parser.PLAIN + function.Default = true + fakeDefault = true + } + + var polyinputs, polyName = function.PossiblePolymorphicDerivations(false) + + var polyinputsSelf []string + + var c, _ = function.Class() + if function.Default && c.Module != parser.MOC { + polyinputsSelf, _ = function.PossibleDerivationsReversedAndRemovedPure(true) + } else { + polyinputsSelf, _ = function.PossiblePolymorphicDerivations(true) + } + + if c.Module == parser.MOC { + var fc, ok = function.Class() + if !ok { + return "" + } + + for _, bcn := range append([]string{function.ClassName()}, fc.GetBases()...) { + var bc, ok = parser.State.ClassMap[bcn] + if !ok { + continue + } + var f = *function + f.Fullname = fmt.Sprintf("%v::%v", bcn, f.Name) + + var ff = bc.GetFunction(f.Name) + for _, fb := range parser.IsBlockedDefault() { + if f.Fullname == fb || (ff != nil && ff.Virtual == parser.PURE && (bc.Module != parser.MOC && bc.Pkg == "")) { + return "" + } + } + } + } + + if (len(polyinputsSelf) == 0 && len(polyinputs) == 0) || + function.SignalMode == parser.CONNECT || function.SignalMode == parser.DISCONNECT || + (len(polyinputsSelf) != 0 && function.Meta == parser.CONSTRUCTOR) || (function.Meta == parser.DESTRUCTOR || strings.HasPrefix(function.Name, parser.TILDE)) { + out := cppFunctionBodyInternal(function) + if fakeDefault { + out = strings.Replace(out, "->"+parser.State.ClassMap[function.ClassName()].GetBases()[0]+"::", "->", -1) + } + return out + } + + bb := new(bytes.Buffer) + defer bb.Reset() + + bb.WriteString("\t") + + var deduce = func(input []string, polyName string, inner bool, body string) string { + bbi := new(bytes.Buffer) + defer bbi.Reset() + for _, polyType := range input { + if polyType == "QObject" || polyType == input[len(input)-1] { + continue + } + + if strings.HasPrefix(polyType, "QMac") { + fmt.Fprint(bbi, "\n\t#ifdef Q_OS_OSX\n\t\t") + } + + base := input[len(input)-1] + if parser.State.ClassMap[polyType].IsSubClassOfQObject() { + base = "QObject" + } + + fmt.Fprintf(bbi, "if (dynamic_cast<%v*>(static_cast<%v*>(%v))) {\n", polyType, base, polyName) + + if strings.HasPrefix(polyType, "QMac") { + fmt.Fprint(bbi, "\t#else\n\t\tif (false) {\n\t#endif\n") + } + + fmt.Fprintf(bbi, "\t%v\n", func() string { + var ibody string + if function.Default && polyName == "ptr" { + if fakeDefault && !inner { + ibody = strings.Replace(body, "static_cast<"+input[len(input)-1]+"*>("+polyName+")->"+input[len(input)-1]+"::", "static_cast("+polyName+")->My"+polyType+"::", -1) + + //TODO: only temporary until invoke works -> + for _, s := range append(parser.State.ClassMap["QAbstractItemView"].GetAllDerivations(), "QAbstractItemView") { + if strings.Contains(ibody, "static_cast(ptr)->My"+s+"::update()") { + ibody = "" + break + } + } + //<- + } else { + ibody = strings.Replace(body, "static_cast<"+input[len(input)-1]+"*>("+polyName+")->"+input[len(input)-1]+"::", "static_cast<"+polyType+"*>("+polyName+")->"+polyType+"::", -1) + } + } else { + ibody = strings.Replace(body, "static_cast<"+input[len(input)-1]+"*>("+polyName+")", "static_cast<"+polyType+"*>("+polyName+")", -1) + } + + if strings.HasPrefix(polyType, "QMac") { + ibody = fmt.Sprintf("#ifdef Q_OS_OSX\n\t%v\n\t#endif", ibody) + } + + if inner { + return ibody + } + if strings.Count(ibody, "\n") > 1 { + return "\t" + strings.Replace(ibody, "\n", "\n\t", -1) + } + return ibody + }()) + fmt.Fprint(bbi, "\t} else ") + } + + if len(input) > 0 { + var _, ok = parser.State.ClassMap[input[len(input)-1]] + if ok { + var f = *function + f.Fullname = fmt.Sprintf("%v::%v", input[len(input)-1], f.Name) + + for _, fb := range parser.IsBlockedDefault() { + if f.Fullname == fb { + body = "" + } + } + } + } + + if bbi.String() == "" { + return body + } + fmt.Fprintf(bbi, "{\n\t%v\n\t}", func() string { + if fakeDefault && !inner { + body = strings.Replace(body, "static_cast<"+function.ClassName()+"*>(ptr)->"+function.ClassName()+"::", "static_cast(ptr)->My"+function.ClassName()+"::", -1) + } + if strings.Count(body, "\n") > 1 { + return "\t" + strings.Replace(body, "\n", "\n\t", -1) + } + return body + }()) + return bbi.String() + } + + if function.Static { + fmt.Fprint(bb, deduce(polyinputs, polyName, true, cppFunctionBodyInternal(function))) + } else if function.Meta == parser.GETTER || function.Meta == parser.SETTER || function.Meta == parser.SLOT { + fmt.Fprint(bb, deduce(polyinputsSelf, "ptr", false, cppFunctionBodyInternal(function))) + } else { + fmt.Fprint(bb, deduce(polyinputsSelf, "ptr", false, deduce(polyinputs, polyName, true, cppFunctionBodyInternal(function)))) + } + + return bb.String() +} + +func cppFunctionBodyInternal(function *parser.Function) string { + + switch function.Meta { + case parser.CONSTRUCTOR: + { + return fmt.Sprintf("%v\treturn %vnew %v%v(%v)%v;", + + func() string { + if parser.State.ClassMap[function.ClassName()].IsSubClassOf("QCoreApplication") || function.Name == "QAndroidService" { + if UseJs() { + return ` static int argcs = argc; + static char** argvs = static_cast(malloc(argcs * sizeof(char*))); + + QList aList = QString::fromStdString(argv["data"].as()).toUtf8().split('|'); + for (int i = 0; i < argcs; i++) + argvs[i] = (new QByteArray(aList.at(i)))->data(); + +` + } + return ` static int argcs = argc; + static char** argvs = static_cast(malloc(argcs * sizeof(char*))); + + QList aList = QByteArray(argv).split('|'); + for (int i = 0; i < argcs; i++) + argvs[i] = (new QByteArray(aList.at(i)))->data(); + +` + } + return "" + }(), + + func() string { + if UseJs() && function.BoundByEmscripten { + return "reinterpret_cast(" + } + return "" + }(), + + func() string { + var class, _ = function.Class() + if class.Module != parser.MOC { + if class.HasCallbackFunctions() { + return "My" + } + } + return "" + }(), + + func() string { + if c := parser.State.ClassMap[function.ClassName()]; c != nil && c.Fullname != "" { + return c.Fullname + } + return function.ClassName() + }(), + + converter.CppInputParameters(function), + + func() string { + if UseJs() && function.BoundByEmscripten { + return ")" + } + return "" + }(), + ) + } + + case parser.SLOT: + { + var ( + functionOutputType string + bb = new(bytes.Buffer) + ) + defer bb.Reset() + + if reg := converter.CppRegisterMetaType(function); reg != "" { + bb.WriteString(reg + "\n") + } + + fmt.Fprint(bb, "\t") + + if converter.CppHeaderOutput(function) != parser.VOID { + functionOutputType = converter.CppInputParametersForSlotArguments(function, &parser.Parameter{Name: "returnArg", Value: function.Output}) + if function.Output != "void*" && !parser.State.ClassMap[strings.TrimSuffix(functionOutputType, "*")].IsSubClassOfQObject() { + functionOutputType = strings.TrimSuffix(functionOutputType, "*") + } + fmt.Fprintf(bb, "%v returnArg;\n\t", functionOutputType) + } + + fmt.Fprintf(bb, "QMetaObject::invokeMethod(static_cast<%v*>(ptr), \"%v\"%v%v);", + + function.ClassName(), + + function.Name, + + func() string { + if converter.CppHeaderOutput(function) != parser.VOID { + + if c, _ := function.Class(); c.Module == parser.MOC && parser.IsPackedMap(function.Output) && function.IsMocFunction { + var tHash = sha1.New() + tHash.Write([]byte(function.Output)) + return fmt.Sprintf(", Q_RETURN_ARG(%v, returnArg)", strings.Replace(functionOutputType, parser.CleanValue(function.Output), fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])), -1)) + } + + return fmt.Sprintf(", Q_RETURN_ARG(%v, returnArg)", functionOutputType) + } + return "" + }(), + + converter.CppInputParametersForSlotInvoke(function), + ) + + if converter.CppHeaderOutput(function) != parser.VOID { + fmt.Fprintf(bb, "\n\treturn %v;", converter.CppOutput("returnArg", functionOutputType, function)) + } + + return bb.String() + } + + case parser.PLAIN, parser.DESTRUCTOR: + { + if (function.Meta == parser.DESTRUCTOR || strings.HasPrefix(function.Name, parser.TILDE)) && function.Default { + return "" + } + + if function.Fullname == "SailfishApp::application" || function.Fullname == "SailfishApp::main" { + return fmt.Sprintf(` static int argcs = argc; + static char** argvs = static_cast(malloc(argcs * sizeof(char*))); + + QList aList = QByteArray(argv).split('|'); + for (int i = 0; i < argcs; i++) + argvs[i] = (new QByteArray(aList.at(i)))->data(); + + return %v(%v);`, + + function.Fullname, + + converter.CppInputParameters(function), + ) + } + + return fmt.Sprintf("\t%v%v;", + + func() string { + if converter.CppHeaderOutput(function) != parser.VOID { + return "return " + } + return "" + }(), + + converter.CppOutputParameters(function, fmt.Sprintf("%v%v%v(%v)%v", + + func() string { + var c, _ = function.Class() + //TODO: + if c.Name == "QAndroidJniEnvironment" && function.Meta == parser.PLAIN && strings.HasPrefix(function.Name, "Exception") { + return "({ QAndroidJniEnvironment env; env->" + } + if function.NonMember { + return "" + } + if function.Static { + return fmt.Sprintf("%v::", function.ClassName()) + } + return fmt.Sprintf("static_cast<%v*>(ptr)->", + func() string { + if c.Fullname != "" { + return c.Fullname + } + if strings.HasSuffix(function.Name, "_atList") { + if function.IsMap { + return fmt.Sprintf("%v<%v,%v>", parser.CleanValue(function.Container), function.Parameters[0].Value, strings.TrimPrefix(function.Output, "const ")) + } + return fmt.Sprintf("%v<%v>", parser.CleanValue(function.Container), strings.TrimPrefix(function.Output, "const ")) + } + if strings.HasSuffix(function.Name, "_setList") { + if len(function.Parameters) == 2 { + return fmt.Sprintf("%v<%v,%v>", parser.CleanValue(function.Container), function.Parameters[0].Value, strings.TrimPrefix(function.Parameters[1].Value, "const ")) + } + return fmt.Sprintf("%v<%v>", parser.CleanValue(function.Container), strings.TrimPrefix(function.Parameters[0].Value, "const ")) + } + if strings.HasSuffix(function.Name, "_newList") { + //will be overriden + } + if strings.HasSuffix(function.Name, "_keyList") { + //will be overriden + } + return function.ClassName() + }(), + ) + }(), + + func() string { + if function.Default { + var c, _ = function.Class() + if c.Module == parser.MOC { + if function.IsMocProperty { + return fmt.Sprintf("%vDefault", function.Name) + } + return fmt.Sprintf("%v::%v", parser.State.ClassMap[function.ClassName()].GetBases()[0], function.Name) + } else { + return fmt.Sprintf("%v::%v", function.ClassName(), function.Name) + } + } + return function.Name + }(), + + converter.CppOutputParametersDeducedFromGeneric(function), converter.CppInputParameters(function), + //TODO: + func() string { + var c, _ = function.Class() + if c.Name == "QAndroidJniEnvironment" && function.Meta == parser.PLAIN && strings.HasPrefix(function.Name, "Exception") { + return "; })" + } + return "" + }()))) + } + + case parser.GETTER: + { + return fmt.Sprintf("\treturn %v;", converter.CppOutputParameters(function, + func() string { + if function.Static { + return function.Fullname + } + return fmt.Sprintf("static_cast<%v*>(ptr)->%v", func() string { + if c := parser.State.ClassMap[function.ClassName()]; c != nil && c.Fullname != "" { + return c.Fullname + } + return function.ClassName() + }(), function.Name) + }())) + } + + case parser.SETTER: + { + var function = *function + function.Name = function.TmpName + function.Fullname = fmt.Sprintf("%v::%v", function.ClassName(), function.Name) + + return fmt.Sprintf("\t%v = %v;", converter.CppOutputParameters(&function, + func() string { + if function.Static { + return function.Fullname + } + return fmt.Sprintf("static_cast<%v*>(ptr)->%v", func() string { + if c := parser.State.ClassMap[function.ClassName()]; c != nil && c.Fullname != "" { + return c.Fullname + } + return function.ClassName() + }(), function.Name) + }()), + + converter.CppInputParameters(&function), + ) + } + + case parser.SIGNAL: + { + var bb = new(bytes.Buffer) + defer bb.Reset() + + if function.SignalMode == parser.CONNECT { + if reg := converter.CppRegisterMetaType(function); reg != "" { + bb.WriteString(reg + "\n") + } + } + + var my string + var c, _ = function.Class() + if c.Module != parser.MOC { + my = "My" + } + if converter.IsPrivateSignal(function) { + fmt.Fprintf(bb, "\tQObject::%v(static_cast<%v*>(ptr), &%v::%v, static_cast<%v%v*>(ptr), static_cast<%v (%v%v::*)(%v)>(&%v%v::Signal_%v%v));", strings.ToLower(function.SignalMode), function.ClassName(), function.ClassName(), function.Name, my, function.ClassName(), function.Output, my, function.ClassName(), converter.CppInputParametersForSignalConnect(function), my, function.ClassName(), strings.Title(function.Name), function.OverloadNumber) + } else { + fmt.Fprintf(bb, "\tQObject::%v(static_cast<%v*>(ptr), static_cast<%v (%v::*)(%v)>(&%v::%v), static_cast<%v%v*>(ptr), static_cast<%v (%v%v::*)(%v)>(&%v%v::Signal_%v%v));", + strings.ToLower(function.SignalMode), + + function.ClassName(), function.Output, function.ClassName(), converter.CppInputParametersForSignalConnect(function), function.ClassName(), function.Name, + + my, function.ClassName(), function.Output, my, function.ClassName(), converter.CppInputParametersForSignalConnect(function), my, function.ClassName(), strings.Title(function.Name), function.OverloadNumber) + } + return bb.String() + } + } + + function.Access = "unsupported_cppFunctionBody" + return function.Access +} diff --git a/qt/tool-chain/binding/templater/function_go.go b/qt/tool-chain/binding/templater/function_go.go new file mode 100644 index 0000000..5a1329a --- /dev/null +++ b/qt/tool-chain/binding/templater/function_go.go @@ -0,0 +1,661 @@ +package templater + +import ( + "bytes" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func goFunction(function *parser.Function) string { + var o = fmt.Sprintf("%v{\n%v\n}", goFunctionHeader(function), goFunctionBody(function)) + var c, _ = function.Class() + if !function.IsSupported() || (c.Stub && function.SignalMode == parser.CALLBACK) { + return "" + } + return o +} + +func goFunctionHeader(function *parser.Function) string { + return fmt.Sprintf("func %v %v(%v)%v", + func() string { + if function.Static || function.Meta == parser.CONSTRUCTOR || function.SignalMode == parser.CALLBACK { + return "" + } + return fmt.Sprintf("(ptr *%v)", function.ClassName()) + }(), + + converter.GoHeaderName(function), + converter.GoHeaderInput(function), + converter.GoHeaderOutput(function), + ) +} + +func goFunctionBody(function *parser.Function) string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + var class, _ = function.Class() + + if class.Stub { + //TODO: collect --> + if converter.GoHeaderOutput(function) == "" { + return bb.String() + } + + fmt.Fprintf(bb, "\nreturn %v%v", + converter.GoOutputParametersFromCFailed(function), + func() string { + if !function.Exception { + return "" + } + if strings.Contains(converter.GoHeaderOutput(function), ",") { + return ", nil" + } + return "nil" + }(), + ) + return bb.String() + //<-- + } + + if utils.QT_DEBUG() { + fmt.Fprintf(bb, "qt.Debug(\"\t%v%v%v(%v) %v\")\n", + class.Name, + strings.Repeat(" ", 45-len(class.Name)), + converter.GoHeaderName(function), + converter.GoHeaderInput(function), + converter.GoHeaderOutput(function), + ) + } + + if !(function.Static || function.Meta == parser.CONSTRUCTOR || function.SignalMode == parser.CALLBACK || strings.Contains(function.Name, "_newList")) { + fmt.Fprintf(bb, "if ptr.Pointer() != nil {\n") + } + + if class.Name == "QAndroidJniObject" { + + if strings.HasPrefix(function.Name, parser.TILDE) { + fmt.Fprint(bb, "qt.DisconnectAllSignals(ptr.Pointer(), \"\")\n") + } + + for _, parameter := range function.Parameters { + if parameter.Value == "..." { + for i := 0; i < 10; i++ { + fmt.Fprintf(bb, "p%v, d%v := assertion(%v, v...)\n", i, i, i) + fmt.Fprintf(bb, "if d%v != nil {\ndefer d%v()\n}\n", i, i) + } + } else if parameter.Value == "T" && function.TemplateModeJNI == "" { + fmt.Fprintf(bb, "p0, d0 := assertion(0, %v)\n", parameter.Name) + fmt.Fprint(bb, "if d0 != nil {\ndefer d0()\n}\n") + } + } + } + + if UseJs() { + for _, alloc := range converter.GoInputParametersForJSAlloc(function) { + fmt.Fprint(bb, alloc) + } + } else { + for _, alloc := range converter.GoInputParametersForCAlloc(function) { + fmt.Fprint(bb, alloc) + } + } + + if function.SignalMode == "" || (function.Meta == parser.SIGNAL && function.SignalMode != parser.CALLBACK) { + + //TODO: --> + if function.SignalMode == parser.CONNECT { + fmt.Fprintf(bb, "\nif !qt.ExistsSignal(ptr.Pointer(), \"%v%v\") {\n", + function.Name, + function.OverloadNumber, + ) + } + + var body string + if UseJs() { + body = converter.GoJSOutputParametersFromC(function, fmt.Sprintf("qt.WASM.Call(\"_%v\", %v)", converter.CppHeaderName(function), converter.GoInputParametersForJS(function))) + } else { + body = converter.GoOutputParametersFromC(function, fmt.Sprintf("C.%v(%v)", converter.CppHeaderName(function), converter.GoInputParametersForC(function))) + } + fmt.Fprint(bb, func() string { + if function.IsMocFunction && function.SignalMode == "" { + for i, p := range function.Parameters { + if p.PureGoType != "" && !parser.IsBlackListedPureGoType(p.PureGoType) { + if UseJs() { + if parser.UseWasm() { + fmt.Fprintf(bb, "%vTID := (time.Now().UnixNano()+(%v*1e10))/1e9\n", parser.CleanName(p.Name, p.Value), i) //TODO: use real pointer for wasm instead ? + } else { + fmt.Fprintf(bb, "%vTID := time.Now().UnixNano()+%v\n", parser.CleanName(p.Name, p.Value), i) + } + fmt.Fprintf(bb, "qt.RegisterTemp(unsafe.Pointer(uintptr(%[1]vTID)), %[1]v)\n", parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, "qt.RegisterTemp(unsafe.Pointer(%[1]v%[2]v), %[2]v)\n", + func() string { + if !strings.HasPrefix(p.PureGoType, "*") { + return "&" + } + return "" + }(), + parser.CleanName(p.Name, p.Value)) + } + } + } + + if function.PureGoOutput != "" && !parser.IsBlackListedPureGoType(function.PureGoOutput) { + bb := new(bytes.Buffer) + defer bb.Reset() + fmt.Fprintf(bb, "oP := unsafe.Pointer(%v)\n", body) + fmt.Fprint(bb, "if oI, ok := qt.ReceiveTemp(oP); ok {\n") + fmt.Fprintf(bb, "oD := oI.(%v)\n", function.PureGoOutput) + if !function.IsMocProperty { + fmt.Fprint(bb, "qt.UnregisterTemp(oP)\n") + } + fmt.Fprint(bb, "return oD\n") + fmt.Fprint(bb, "}\n") + return bb.String() + } + } + + if converter.GoHeaderOutput(function) == "" { + return body + } + + switch { + case function.NeedsFinalizer && parser.State.ClassMap[parser.CleanValue(function.Output)].IsSupported() || function.Meta == parser.CONSTRUCTOR && !(parser.State.ClassMap[function.Name].HasCallbackFunctions() || parser.State.ClassMap[function.Name].IsSubClassOfQObject()): + { + var bb = new(bytes.Buffer) + defer bb.Reset() + + fmt.Fprintf(bb, "tmpValue := %v\n", body) + + if class.Name != "QAndroidJniObject" || class.Name == "QAndroidJniObject" && (function.TemplateModeJNI == "String" || function.Output == "QAndroidJniObject" || function.Meta == parser.CONSTRUCTOR) { + if function.TemplateModeJNI == "String" { + fmt.Fprint(bb, "tmpValueToString := tmpValue.ToString()\n") + fmt.Fprint(bb, "tmpValue.DestroyQAndroidJniObject()\n") + } else { + class.HasFinalizer = true + fmt.Fprintf(bb, "runtime.SetFinalizer(tmpValue, (%v).Destroy%v)\n", + func() string { + if function.TemplateModeJNI != "" { + return fmt.Sprintf("*%v", parser.CleanValue(function.Output)) + } + return converter.GoHeaderOutput(function) + }(), + + func() string { + if function.Meta == parser.CONSTRUCTOR { + return function.Name + } + return parser.CleanValue(function.Output) + }(), + ) + } + } + + fmt.Fprintf(bb, "return tmpValue%v%v", + func() string { + if function.TemplateModeJNI == "String" { + return "ToString" + } + return "" + }(), + + func() string { + if function.Exception { + return ", QAndroidJniEnvironment_ExceptionCatch()" + } + return "" + }()) + + return bb.String() + } + + case parser.State.ClassMap[parser.CleanValue(function.Output)].IsSubClassOfQObject() && converter.GoHeaderOutput(function) != "unsafe.Pointer" || function.Meta == parser.CONSTRUCTOR && parser.State.ClassMap[parser.CleanValue(function.Name)].IsSubClassOfQObject(): + { + var bb = new(bytes.Buffer) + defer bb.Reset() + + fmt.Fprintf(bb, "tmpValue := %v\n", body) + + if class.Name != "SailfishApp" { + fmt.Fprintf(bb, "if !qt.ExistsSignal(tmpValue.Pointer(), \"destroyed\") {\ntmpValue.ConnectDestroyed(func(%v){ tmpValue.SetPointer(nil) })\n}\n", + func() string { + if class.Module == "QtCore" { + return "*QObject" + } + return "*core.QObject" + }()) + } + + /* TODO: re-implement for custom constructors + var class, _ = function.Class() + if class.Module == parser.MOC && function.Meta == parser.CONSTRUCTOR { + if len(class.Constructors) > 0 { + fmt.Fprintf(bb, "tmpValue.%v()\n", class.Constructors[0]) + } + } + */ + + fmt.Fprint(bb, "return tmpValue") + + return bb.String() + } + + default: + { + if function.Name == "readData" && len(function.Parameters) == 2 { + if UseJs() { + return "" + } + return fmt.Sprintf("ret := %v\nif ret > 0 {\n*data = C.GoStringN(dataC, C.int(ret))\n}\nreturn ret", body) + } else { + if function.Exception && converter.GoHeaderOutput(function) == "(error)" { + return fmt.Sprintf("%v\nreturn QAndroidJniEnvironment_ExceptionCatch()", body) + } + + return fmt.Sprintf("return %v%v", body, + func() string { + if function.Exception { + return ", QAndroidJniEnvironment_ExceptionCatch()" + } + return "" + }()) + } + } + } + + }()) + + if function.SignalMode == parser.CONNECT { + fmt.Fprint(bb, "\n}\n") + } + } + //<-- + + switch function.SignalMode { + case parser.CALLBACK: + { + headerOutputFakeFunc := *function + headerOutputFakeFunc.SignalMode = "" + + if parser.UseWasm() { + bb.WriteString("ptr := uintptr(args[0].Int())\n") + } + if parser.UseJs() { + for i, p := range function.Parameters { + if !(function.Name == "readData" && len(function.Parameters) == 2) { //TODO: + if parser.UseWasm() { + conv := converter.GoOutputJS(fmt.Sprintf("args[%v]", i+1), p.Value, function, function.PureGoOutput) + if strings.Contains(conv, "func(") { + fmt.Fprintf(bb, "%v := %v\n", parser.CleanName(p.Name, p.Value), fmt.Sprintf("args[%v]", i+1)) + } else if converter.GoType(function, p.Value, p.PureGoType) == "*bool" { + fmt.Fprintf(bb, "%v := uintptr(args[%v].Int())\n", parser.CleanName(p.Name, p.Value), i+1) + } else { + fmt.Fprintf(bb, "%v := %v\n", parser.CleanName(p.Name, p.Value), conv) + } + } else { + conv := converter.GoOutputJS(parser.CleanName(p.Name, p.Value)+"P", p.Value, function, function.PureGoOutput) + if strings.Contains(conv, "jsGoUnpackString") { + fmt.Fprintf(bb, "%v := %v\n", parser.CleanName(p.Name, p.Value), conv) + } + } + } + } + } + + if function.IsMocFunction { + for _, p := range function.Parameters { + if p.PureGoType != "" && !parser.IsBlackListedPureGoType(p.PureGoType) { + fmt.Fprintf(bb, "var %vD %v\n", parser.CleanName(p.Name, p.Value), p.PureGoType) + fmt.Fprintf(bb, "if %[1]vI, ok := qt.ReceiveTemp(unsafe.Pointer(uintptr(%[1]v))); ok {\n", parser.CleanName(p.Name, p.Value)) + if !strings.HasSuffix(function.Name, "Changed") { //TODO: check if property instead + fmt.Fprintf(bb, "qt.UnregisterTemp(unsafe.Pointer(uintptr(%v)))\n", parser.CleanName(p.Name, p.Value)) + } + fmt.Fprintf(bb, "%[1]vD = %[1]vI.(%v)\n", parser.CleanName(p.Name, p.Value), p.PureGoType) + fmt.Fprint(bb, "}\n") + } + } + } + + for _, p := range function.Parameters { + if converter.GoType(function, p.Value, p.PureGoType) == "*bool" { + if UseJs() { + fmt.Fprintf(bb, "%vR := int8(qt.WASM.Call(\"getValue\", %v, \"i8\").Int()) != 0\ndefer func(){qt.WASM.Call(\"setValue\", %v, qt.GoBoolToInt(%vR), \"i8\")}()\n", parser.CleanName(p.Name, p.Value), parser.CleanName(p.Name, p.Value), parser.CleanName(p.Name, p.Value), parser.CleanName(p.Name, p.Value)) + } else { + fmt.Fprintf(bb, "%vR := %v\ndefer func(){*%v = %v}()\n", parser.CleanName(p.Name, p.Value), converter.GoOutput("*"+parser.CleanName(p.Name, p.Value), p.Value, function, p.PureGoType), parser.CleanName(p.Name, p.Value), converter.GoInput(parser.CleanName(p.Name, p.Value)+"R", strings.Replace(p.Value, "*", "", -1), function, p.PureGoType)) + } + } + } + + // + + if UseJs() { + fmt.Fprintf(bb, "if signal := qt.GetSignal(unsafe.Pointer(ptr), \"%v%v\"); signal != nil {\n", + function.Name, + function.OverloadNumber, + ) + } else { + fmt.Fprintf(bb, "if signal := qt.GetSignal(ptr, \"%v%v\"); signal != nil {\n", + function.Name, + function.OverloadNumber, + ) + } + + if converter.GoHeaderOutput(&headerOutputFakeFunc) == "" { + //TODO wasm: wait for fix to https://github.com/golang/go/issues/26045#issuecomment-400017599 + fmt.Fprintf(bb, "signal.(%v)(%v)", converter.GoHeaderInputSignalFunction(function), converter.GoInputParametersForCallback(function)) + } else { + if function.Name == "readData" && len(function.Parameters) == 2 { + if !UseJs() { + fmt.Fprint(bb, "retS := cGoUnpackString(data)\n") + fmt.Fprintf(bb, "ret := %v\n", converter.GoInput(fmt.Sprintf("signal.(%v)(%v)", converter.GoHeaderInputSignalFunction(function), converter.GoInputParametersForCallback(function)), function.Output, function, function.PureGoOutput)) + fmt.Fprint(bb, "if ret > 0 {\nC.memcpy(unsafe.Pointer(data.data), unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&retS)).Data), C.size_t(ret))\n}\n") + fmt.Fprint(bb, "return ret") + } else { + fmt.Fprint(bb, "return 0") + } + } else { + if function.IsMocFunction && function.PureGoOutput != "" && !parser.IsBlackListedPureGoType(function.PureGoOutput) { + fmt.Fprintf(bb, "oP := %v\n", fmt.Sprintf("signal.(%v)(%v)", converter.GoHeaderInputSignalFunction(function), converter.GoInputParametersForCallback(function))) + fmt.Fprintf(bb, "qt.RegisterTemp(unsafe.Pointer(%voP), oP)\n", func() string { + if !strings.HasPrefix(function.PureGoOutput, "*") { + return "&" + } + return "" + }()) + fmt.Fprintf(bb, "return %v", converter.GoInput(fmt.Sprintf("uintptr(unsafe.Pointer(%voP))", func() string { + if !strings.HasPrefix(function.PureGoOutput, "*") { + return "&" + } + return "" + }()), function.Output, function, function.PureGoOutput)) + } else { + if (parser.CleanValue(function.Output) == "QString" || parser.CleanValue(function.Output) == "QStringList") && !parser.UseJs() { + fmt.Fprintf(bb, "tempVal := signal.(%v)(%v)\n", converter.GoHeaderInputSignalFunction(function), converter.GoInputParametersForCallback(function)) + fmt.Fprintf(bb, "return C.struct_%v_PackedString{data: %v, len: %v}", strings.Title(parser.State.ClassMap[function.ClassName()].Module), converter.GoInput("tempVal", function.Output, function, function.PureGoOutput), + func() string { + if parser.IsBlackListedPureGoType(function.PureGoOutput) { + return "C.longlong(-1)" + } + if parser.CleanValue(function.Output) == "QStringList" { + return "C.longlong(len(strings.Join(tempVal, \"|\")))" + } + return "C.longlong(len(tempVal))" + }()) + } else { + fmt.Fprintf(bb, "return %v", converter.GoInput(fmt.Sprintf("signal.(%v)(%v)", converter.GoHeaderInputSignalFunction(function), converter.GoInputParametersForCallback(function)), function.Output, function, function.PureGoOutput)) + } + } + } + } + + fmt.Fprintf(bb, "\n}%v\n", + func() string { + if converter.GoHeaderOutput(&headerOutputFakeFunc) == "" { + if (!function.IsDerivedFromPure() || function.IsDerivedFromImpure() || function.Synthetic) && function.Meta != parser.SIGNAL { + return "else{" + } + } + return "" + }(), + ) + + if converter.GoHeaderOutput(&headerOutputFakeFunc) == "" { + if (!function.IsDerivedFromPure() || function.IsDerivedFromImpure() || function.Synthetic) && function.Meta != parser.SIGNAL { + if UseJs() { + if parser.UseWasm() { + //TODO: workaround for https://github.com/golang/go/issues/26045#issuecomment-400017599 + fmt.Fprintf(bb, "go New%vFromPointer(unsafe.Pointer(ptr)).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)) + } else { + fmt.Fprintf(bb, "New%vFromPointer(unsafe.Pointer(ptr)).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)) + } + } else { + fmt.Fprintf(bb, "New%vFromPointer(ptr).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)) + } + } + } else { + if (!function.IsDerivedFromPure() || function.IsDerivedFromImpure() || function.Synthetic) && function.Meta != parser.SIGNAL { + if function.Name == "readData" && len(function.Parameters) == 2 { + if !UseJs() { + fmt.Fprint(bb, "retS := cGoUnpackString(data)\n") + fmt.Fprintf(bb, "ret := %v\n", converter.GoInput(fmt.Sprintf("New%vFromPointer(ptr).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)), function.Output, function, function.PureGoOutput)) + fmt.Fprint(bb, "if ret > 0 {\nC.memcpy(unsafe.Pointer(data.data), unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&retS)).Data), C.size_t(ret))\n}\n") + fmt.Fprint(bb, "return ret") + } else { + fmt.Fprint(bb, "return 0") + } + } else { + if function.IsMocFunction && function.IsMocProperty && function.PureGoOutput != "" && !parser.IsBlackListedPureGoType(function.PureGoOutput) { + if UseJs() { + fmt.Fprintf(bb, "oP := %v\n", fmt.Sprintf("New%vFromPointer(unsafe.Pointer(ptr)).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function))) + } else { + fmt.Fprintf(bb, "oP := %v\n", fmt.Sprintf("New%vFromPointer(ptr).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function))) + } + fmt.Fprintf(bb, "qt.RegisterTemp(unsafe.Pointer(%voP), oP)\n", func() string { + if !strings.HasPrefix(function.PureGoOutput, "*") { + return "&" + } + return "" + }()) + fmt.Fprintf(bb, "return %v", converter.GoInput(fmt.Sprintf("uintptr(unsafe.Pointer(%voP))", func() string { + if !strings.HasPrefix(function.PureGoOutput, "*") { + return "&" + } + return "" + }()), function.Output, function, function.PureGoOutput)) + } else { + if (parser.CleanValue(function.Output) == "QString" || parser.CleanValue(function.Output) == "QStringList") && !parser.UseJs() { + if UseJs() { + fmt.Fprintf(bb, "tempVal := New%vFromPointer(unsafe.Pointer(ptr)).%v%vDefault(%v)\n", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)) + } else { + fmt.Fprintf(bb, "tempVal := New%vFromPointer(ptr).%v%vDefault(%v)\n", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)) + } + fmt.Fprintf(bb, "return C.struct_%v_PackedString{data: %v, len: %v}", strings.Title(parser.State.ClassMap[function.ClassName()].Module), converter.GoInput("tempVal", function.Output, function, function.PureGoOutput), + func() string { + if parser.IsBlackListedPureGoType(function.PureGoOutput) { + return "C.longlong(-1)" + } + if parser.CleanValue(function.Output) == "QStringList" { + return "C.longlong(len(strings.Join(tempVal, \"|\")))" + } + return "C.longlong(len(tempVal))" + }()) + } else { + if UseJs() { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(fmt.Sprintf("New%vFromPointer(unsafe.Pointer(ptr)).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)), function.Output, function, function.PureGoOutput)) + } else { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(fmt.Sprintf("New%vFromPointer(ptr).%v%vDefault(%v)", strings.Title(class.Name), strings.Replace(strings.Title(function.Name), parser.TILDE, "Destroy", -1), function.OverloadNumber, converter.GoInputParametersForCallback(function)), function.Output, function, function.PureGoOutput)) + } + } + } + } + } else { + if converter.GoOutputParametersFromCFailed(function) == "nil" { + var ( + class, _ = function.Class() + found bool + c, ok = parser.State.ClassMap[parser.CleanValue(function.Output)] + ) + if ok && c.IsSupported() && !hasUnimplementedPureVirtualFunctions(c.Name) { + for _, f := range c.Functions { + if f.Meta == parser.CONSTRUCTOR && len(f.Parameters) == 0 { + if c.Module == class.Module { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(fmt.Sprintf("%v()", converter.GoHeaderName(f)), function.Output, function, function.PureGoOutput)) + } else { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(fmt.Sprintf("%v.%v()", goModule(c.Module), converter.GoHeaderName(f)), function.Output, function, function.PureGoOutput)) + } + found = true + break + } + } + if !found { + for _, f := range c.Functions { + if f.Meta == parser.CONSTRUCTOR && len(f.Parameters) == 1 { + if c.Module == class.Module { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(fmt.Sprintf("%v(%v)", converter.GoHeaderName(f), converter.GoOutputFailed(f.Parameters[0].Value, f, f.Parameters[0].PureGoType)), function.Output, function, function.PureGoOutput)) + } else { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(fmt.Sprintf("%v.%v(%v)", goModule(c.Module), converter.GoHeaderName(f), converter.GoOutputFailed(f.Parameters[0].Value, f, f.Parameters[0].PureGoType)), function.Output, function, function.PureGoOutput)) + } + found = true + break + } + } + } + } + + if !found { + //TODO: + //function.Access = fmt.Sprintf("unsupported_FailedNilOutputInCallbacksForPureVirtualFunctions(%v)", function.Output) + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(converter.GoOutputParametersFromCFailed(function), function.Output, function, function.PureGoOutput)) + } + } else { + if (parser.CleanValue(function.Output) == "QString" || parser.CleanValue(function.Output) == "QStringList") && !parser.UseJs() { + fmt.Fprintf(bb, "tempVal := %v\n", converter.GoOutputParametersFromCFailed(function)) + fmt.Fprintf(bb, "return C.struct_%v_PackedString{data: %v, len: %v}", strings.Title(parser.State.ClassMap[function.ClassName()].Module), converter.GoInput("tempVal", function.Output, function, function.PureGoOutput), + func() string { + if parser.IsBlackListedPureGoType(function.PureGoOutput) { + return "C.longlong(-1)" + } + if parser.CleanValue(function.Output) == "QStringList" { + return "C.longlong(len(strings.Join(tempVal, \"|\")))" + } + return "C.longlong(len(tempVal))" + }()) + } else { + fmt.Fprintf(bb, "\nreturn %v", converter.GoInput(converter.GoOutputParametersFromCFailed(function), function.Output, function, function.PureGoOutput)) + } + } + } + } + + fmt.Fprintf(bb, "%v", + func() string { + if converter.GoHeaderOutput(&headerOutputFakeFunc) == "" { + if (!function.IsDerivedFromPure() || function.IsDerivedFromImpure() || function.Synthetic) && function.Meta != parser.SIGNAL { + return "\n}" + } + } + return "" + }(), + ) + + if parser.UseWasm() { + if converter.GoHeaderOutput(&headerOutputFakeFunc) == "" { + bb.WriteString("\nreturn nil") + } + } + } + + case parser.CONNECT: + { + fmt.Fprintf(bb, "\nif signal := qt.LendSignal(ptr.Pointer(), \"%v%v\"); signal != nil {\n", + function.Name, + function.OverloadNumber, + ) + + fmt.Fprintf(bb, "\tqt.%vSignal(ptr.Pointer(), \"%v%v\", %v)", + function.SignalMode, + function.Name, + function.OverloadNumber, + func() string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + fmt.Fprintf(bb, "%v {\n", strings.TrimPrefix(converter.GoHeaderInput(function), "f ")) + + var f = *function + f.SignalMode = "" + + fmt.Fprintf(bb, "signal.(%v%v)(%v)\n", + converter.GoHeaderInputSignalFunction(&f), + converter.GoHeaderOutput(&f), + converter.GoGoInput(&f), + ) + + if converter.GoHeaderOutput(&f) != "" { + fmt.Fprint(bb, "return ") + } + + fmt.Fprintf(bb, "f(%v)\n", + converter.GoGoInput(&f), + ) + + fmt.Fprint(bb, "}") + + return bb.String() + }()) + + fmt.Fprintf(bb, "} else {\n") + fmt.Fprintf(bb, "\tqt.%vSignal(ptr.Pointer(), \"%v%v\"%v)", + function.SignalMode, + function.Name, + function.OverloadNumber, + func() string { + if function.SignalMode == parser.CONNECT { + return ", f" + } + return "" + }(), + ) + + fmt.Fprintf(bb, "}") + } + + case parser.DISCONNECT: + { + fmt.Fprintf(bb, "\nqt.%vSignal(ptr.Pointer(), \"%v%v\"%v)", + function.SignalMode, + function.Name, + function.OverloadNumber, + func() string { + if function.SignalMode == parser.CONNECT { + return ", f" + } + return "" + }(), + ) + } + } + + if (function.Name == "deleteLater" || strings.HasPrefix(function.Name, parser.TILDE)) && function.SignalMode == "" { + fmt.Fprint(bb, "\nptr.SetPointer(nil)") + if class.HasFinalizer { + fmt.Fprint(bb, "\nruntime.SetFinalizer(ptr, nil)") + } + } + + if !(function.Static || function.Meta == parser.CONSTRUCTOR || function.SignalMode == parser.CALLBACK || strings.Contains(function.Name, "_newList")) { + fmt.Fprint(bb, "\n}") + + if converter.GoHeaderOutput(function) == "" { + return bb.String() + } + + if function.IsMocFunction && function.PureGoOutput != "" && !parser.IsBlackListedPureGoType(function.PureGoOutput) && function.SignalMode == "" { + fmt.Fprintf(bb, "\nvar out %v\nreturn out", function.PureGoOutput) + } else { + //TODO: collect --> + fmt.Fprintf(bb, "\nreturn %v%v", + converter.GoOutputParametersFromCFailed(function), + func() string { + if !function.Exception { + return "" + } + if strings.Contains(converter.GoHeaderOutput(function), ",") { + return ", errors.New(\"*.Pointer() == nil\")" + } + return "errors.New(\"*.Pointer() == nil\")" + }(), + ) + + return bb.String() + //<-- + } + } + + return bb.String() +} diff --git a/qt/tool-chain/binding/templater/helper.go b/qt/tool-chain/binding/templater/helper.go new file mode 100644 index 0000000..595d7c5 --- /dev/null +++ b/qt/tool-chain/binding/templater/helper.go @@ -0,0 +1,74 @@ +package templater + +import ( + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func hasUnimplementedPureVirtualFunctions(className string) bool { + for _, f := range parser.State.ClassMap[className].Functions { + + if !f.Checked { + cppFunction(f) + goFunction(f) + f.Checked = true + } + + if f.Virtual == parser.PURE && !f.IsSupported() { + return true + } + } + return false +} + +func goModule(module string) string { + return strings.ToLower(strings.TrimPrefix(module, "Qt")) +} + +func UseStub(force bool, module string, mode int) bool { + return force || (utils.QT_STUB() && mode == NONE && !(module == "QtAndroidExtras" || module == "QtSailfish")) +} + +func UseJs() bool { return parser.UseJs() } //TODO: remove + +func buildTags(module string, stub bool, mode int, tags string) string { + switch { + case stub: + { + if module == "QtAndroidExtras" || module == "androidextras" { + return "// +build !android,!android_emulator" + } + return "// +build !sailfish,!sailfish_emulator" + } + + case mode == MINIMAL: + { + return "// +build minimal" + } + + case mode == MOC: + { + if tags != "" { + return "// +build " + tags + } + return "" + } + + case module == "QtAndroidExtras", module == "androidextras": + { + return "// +build android android_emulator" + } + + case module == "QtSailfish", module == "sailfish": + { + return "// +build sailfish sailfish_emulator" + } + + default: + { + return "// +build !minimal" + } + } +} diff --git a/qt/tool-chain/binding/templater/template_c.go b/qt/tool-chain/binding/templater/template_c.go new file mode 100644 index 0000000..0cda902 --- /dev/null +++ b/qt/tool-chain/binding/templater/template_c.go @@ -0,0 +1,243 @@ +package templater + +import ( + "bytes" + "fmt" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" +) + +func cTemplate(bb *bytes.Buffer, c *parser.Class, ef func(*parser.Enum, *parser.Value) string, ff func(*parser.Function) string, del string, isGo bool) { + cTemplateEnums(bb, c, ef, del, isGo) + + if c.IsSupported() { + cTemplateFunctions(bb, c, ff, del, isGo) + } +} + +func cTemplateEnums(bb *bytes.Buffer, c *parser.Class, ef func(*parser.Enum, *parser.Value) string, del string, isGo bool) { + for _, enum := range c.Enums { + if isGo { + fmt.Fprintf(bb, "%v%v", ef(enum, nil), del) + } else { + for _, value := range enum.Values { + if converter.EnumNeedsCppGlue(value.Value) { + fmt.Fprintf(bb, "%v%v", ef(enum, value), del) + } + } + } + } +} + +var deferredFunctions []string + +func cTemplateFunctions(bb *bytes.Buffer, pc *parser.Class, ff func(*parser.Function) string, del string, isGo bool) { + + var implemented = make(map[string]struct{}) + + for i, cn := range append([]string{pc.Name}, pc.GetAllBases()...) { + var c, e = parser.State.ClassMap[cn] + if !e || !c.IsSupported() { + continue + } + + for _, f := range c.Functions { + var _, e = implemented[fmt.Sprint(f.Name, f.OverloadNumber)] + if e || !f.IsSupported() { + continue + } + + if !f.Checked { + cppFunction(f) + goFunction(f) + f.Checked = true + } + + if !f.IsSupported() { + continue + } + + if i > 0 && (f.Meta == parser.CONSTRUCTOR || f.Meta == parser.DESTRUCTOR) { + continue + } + + if f.Meta == parser.CONSTRUCTOR && hasUnimplementedPureVirtualFunctions(c.Name) { + continue + } + + var f = *f + + if f.IsDerivedFromImpure() { + f.Virtual = parser.IMPURE + } + + if i > 0 { + f.Synthetic = true + } + + f.Fullname = fmt.Sprintf("%v::%v", pc.Name, f.Name) + + implemented[fmt.Sprint(f.Name, f.OverloadNumber)] = struct{}{} + switch { + case f.Meta == parser.SLOT: + { + if isGo { + for _, signalMode := range []string{parser.CALLBACK, parser.CONNECT, parser.DISCONNECT} { + var f = f + f.SignalMode = signalMode + if !f.Implements() { + break + } + if out := ff(&f); out != "" { + if signalMode == parser.CALLBACK { + fmt.Fprintf(bb, "//export %v\n", converter.GoHeaderName(&f)) + } + fmt.Fprintf(bb, "%v%v", out, del) + } + if i > 0 { + break + } + } + } + + if f.Implements() { + var f = f + if isGo { + f.Meta = parser.PLAIN + } + if UseJs() && del == "\n\n" && !isGo { + deferredFunctions = append(deferredFunctions, fmt.Sprintf("%v%v", ff(&f), del)) + } else { + fmt.Fprintf(bb, "%v%v", ff(&f), del) + } + } + if f.Virtual != parser.PURE || f.IsDerivedFromImpure() || i > 0 { + f.Default = true + if f.Implements() { + f.Meta = parser.PLAIN + fmt.Fprintf(bb, "%v%v", ff(&f), del) + } + } + } + + case f.Meta == parser.SIGNAL: + { + for _, signalMode := range []string{parser.CALLBACK, parser.CONNECT, parser.DISCONNECT} { + var f = f + f.SignalMode = signalMode + + if !isGo && signalMode == parser.CALLBACK { + if i > 0 { + break + } + continue + } + + if !f.Implements() { + break + } + if out := ff(&f); out != "" { + if signalMode == parser.CALLBACK && isGo { + fmt.Fprintf(bb, "//export %v\n", converter.GoHeaderName(&f)) + } + fmt.Fprintf(bb, "%v%v", out, del) + } + if i > 0 { + break + } + } + + if !f.Implements() { + break + } + if i > 0 { + break + } + if !converter.IsPrivateSignal(&f) { + f.Meta = parser.PLAIN + fmt.Fprintf(bb, "%v%v", ff(&f), del) + } + } + + case f.Virtual == parser.IMPURE, f.Virtual == parser.PURE: + { + if isGo { + for _, signalMode := range []string{parser.CALLBACK, parser.CONNECT, parser.DISCONNECT} { + var f = f + f.SignalMode = signalMode + + if !f.Implements() { + break + } + if out := ff(&f); out != "" { + if signalMode == parser.CALLBACK { + fmt.Fprintf(bb, "//export %v\n", converter.GoHeaderName(&f)) + } + fmt.Fprintf(bb, "%v%v", out, del) + } + if i > 0 { + break + } + } + } + + if f.Implements() { + var f = f + f.Meta = parser.PLAIN + fmt.Fprintf(bb, "%v%v", ff(&f), del) + } + if f.Virtual != parser.PURE || f.IsDerivedFromImpure() || i > 0 { + f.Default = true + if f.Implements() { + f.Meta = parser.PLAIN + fmt.Fprintf(bb, "%v%v", ff(&f), del) + } + } + } + + case f.IsJNIGeneric(): + { + if !f.Implements() { + break + } + if i > 0 { + break + } + for _, m := range converter.CppOutputParametersJNIGenericModes(&f) { + f.TemplateModeJNI = m + fmt.Fprintf(bb, "%v%v", ff(&f), del) + + f.Exception = true + fmt.Fprintf(bb, "%v%v", ff(&f), del) + f.Exception = false + } + } + + default: + { + if !f.Implements() { + break + } + if i > 0 { + break + } + var out = ff(&f) + if out != "" { + fmt.Fprintf(bb, "%v%v", out, del) + if isGo && f.Static { + fmt.Fprintf(bb, "%v{\n%v\n}\n\n", + func() string { + var f = f + f.Static = false + return goFunctionHeader(&f) + }(), + goFunctionBody(&f), + ) + } + } + } + } + } + } +} diff --git a/qt/tool-chain/binding/templater/template_cgo.go b/qt/tool-chain/binding/templater/template_cgo.go new file mode 100644 index 0000000..8b1f8c8 --- /dev/null +++ b/qt/tool-chain/binding/templater/template_cgo.go @@ -0,0 +1,244 @@ +package templater + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func cleanLibs(module string, mode int) []string { + + var out []string + + switch { + case mode == RCC: + out = []string{"Core"} + case mode == MOC, module == "build_static": + out = parser.LibDeps[module] + case mode == MINIMAL, mode == NONE: + out = append([]string{module}, parser.LibDeps[module]...) + } + + for i, v := range out { + if v == "Speech" { + out[i] = "TextToSpeech" + } + } + return out +} + +//needed for static linking +func GetiOSClang(buildTarget, _, path string) []string { + var tmp = CgoTemplate("build_static", path, buildTarget, NONE, "main", "") + + tmp = strings.Split(tmp, "/*")[1] + tmp = strings.Split(tmp, "*/")[0] + + tmp = strings.Replace(tmp, "#cgo CFLAGS: ", "", -1) + tmp = strings.Replace(tmp, "#cgo CXXFLAGS: ", "", -1) + tmp = strings.Replace(tmp, "#cgo LDFLAGS: ", "", -1) + tmp = strings.Replace(tmp, "\n", " ", -1) + + if buildTarget == "ios" { + tmp = strings.Replace(tmp, "_iphonesimulator", "", -1) + tmp = strings.Replace(tmp, "x86_64", "arm64", -1) + tmp = strings.Replace(tmp, "iPhoneSimulator", "iPhoneOS", -1) + tmp = strings.Replace(tmp, "ios-simulator", "iphoneos", -1) + } + + return strings.Split(tmp, " ") +} + +func cgoSailfish(module, mocPath string, mode int, pkg string, libs []string) { + var bb = new(bytes.Buffer) + defer bb.Reset() + + if mode != MOC { + libs = cleanLibs(module, mode) + } + + fmt.Fprintf(bb, "// +build ${BUILDTARGET}%v\n\n", func() string { + if mode == MINIMAL { + return ",minimal" + } + if mode == NONE { + return ",!minimal" + } + return "" + }()) + + fmt.Fprintf(bb, "package %v\n\n", func() string { + if mode == MOC { + return pkg + } + return strings.ToLower(module) + }()) + fmt.Fprint(bb, "/*\n") + + fmt.Fprint(bb, "#cgo CFLAGS: -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -m32 -msse -msse2 -march=i686 -mfpmath=sse -mtune=generic -fno-omit-frame-pointer -fasynchronous-unwind-tables -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -Wall -W -D_REENTRANT -fPIC\n") + fmt.Fprint(bb, "#cgo CXXFLAGS: -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -m32 -msse -msse2 -march=i686 -mfpmath=sse -mtune=generic -fno-omit-frame-pointer -fasynchronous-unwind-tables -std=gnu++0x -fPIC -fvisibility=hidden -fvisibility-inlines-hidden -Wall -W -D_REENTRANT -fPIC\n") + + fmt.Fprint(bb, "#cgo CXXFLAGS: -DQT_NO_DEBUG") + for _, m := range libs { + fmt.Fprintf(bb, " -DQT_%v_LIB", strings.ToUpper(m)) + } + fmt.Fprint(bb, "\n") + + fmt.Fprint(bb, "#cgo CXXFLAGS: -I/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/share/qt5/mkspecs/linux-g++ -isystem /srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include -isystem /srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include/sailfishapp -isystem /srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include/mdeclarativecache5 -isystem /srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include/qt5\n") + + fmt.Fprint(bb, "#cgo CXXFLAGS:") + for _, m := range libs { + fmt.Fprintf(bb, " -isystem /srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include/qt5/Qt%v", m) + } + fmt.Fprint(bb, "\n\n") + + fmt.Fprint(bb, "#cgo LDFLAGS: -Wl,-O1 -Wl,-rpath-link,/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/lib -Wl,-rpath-link,/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/lib -Wl,-rpath-link,/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/lib/pulseaudio\n") + + fmt.Fprint(bb, "#cgo LDFLAGS: -rdynamic -L/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/lib -L/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/lib -L/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/lib/pulseaudio -lsailfishapp -lmdeclarativecache5") + for _, m := range libs { + if !(m == "UiPlugin" || m == "Sailfish") { + if parser.IsWhiteListedSailfishLib(m) { + fmt.Fprintf(bb, " -lQt5%v", m) + } + } + } + + fmt.Fprint(bb, " -lGLESv2 -lpthread\n") + fmt.Fprint(bb, "*/\n") + + fmt.Fprint(bb, "import \"C\"\n") + + var tmp = strings.Replace(bb.String(), "${BUILDTARGET}", "sailfish_emulator", -1) + + switch { + case mode == RCC: + { + utils.Save(filepath.Join(mocPath, "rcc_cgo_sailfish_emulator_linux_386.go"), tmp) + } + + case mode == MOC: + { + utils.Save(filepath.Join(mocPath, "moc_cgo_sailfish_emulator_linux_386.go"), tmp) + } + + case mode == MINIMAL: + { + utils.Save(utils.GoQtPkgPath(strings.ToLower(module), "minimal_cgo_sailfish_emulator_linux_386.go"), tmp) + } + + default: + { + utils.Save(utils.GoQtPkgPath(strings.ToLower(module), "cgo_sailfish_emulator_linux_386.go"), tmp) + } + } + + tmp = strings.Replace(bb.String(), "${BUILDTARGET}", "sailfish", -1) + tmp = strings.Replace(tmp, "-m32 -msse -msse2 -march=i686 -mfpmath=sse -mtune=generic -fno-omit-frame-pointer -fasynchronous-unwind-tables", "-fmessage-length=0 -march=armv7-a -mfloat-abi=hard -mfpu=neon -mthumb -Wno-psabi", -1) + tmp = strings.Replace(tmp, "i486", "armv7hl", -1) + + switch { + case mode == RCC: + { + utils.Save(filepath.Join(mocPath, "rcc_cgo_sailfish_linux_arm.go"), tmp) + } + + case mode == MOC: + { + utils.Save(filepath.Join(mocPath, "moc_cgo_sailfish_linux_arm.go"), tmp) + } + + case mode == MINIMAL: + { + utils.Save(utils.GoQtPkgPath(strings.ToLower(module), "minimal_cgo_sailfish_linux_arm.go"), tmp) + } + + default: + { + utils.Save(utils.GoQtPkgPath(strings.ToLower(module), "cgo_sailfish_linux_arm.go"), tmp) + } + } +} + +func cgoAsteroid(module, mocPath string, mode int, pkg string) { + var ( + bb = new(bytes.Buffer) + libs = cleanLibs(module, mode) + ) + defer bb.Reset() + + fmt.Fprintf(bb, "// +build ${BUILDTARGET}%v\n\n", func() string { + if mode == MINIMAL { + return ",minimal" + } + if mode == MOC { + return "" + } + return ",!minimal" + }()) + + fmt.Fprintf(bb, "package %v\n\n", func() string { + if mode == MOC { + return pkg + } + return strings.ToLower(module) + }()) + fmt.Fprint(bb, "/*\n") + + fmt.Fprint(bb, "#cgo CFLAGS: -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -feliminate-unused-debug-types -fexceptions -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -fmessage-length=0 -march=armv7ve -mfloat-abi=softfp -mfpu=neon -mthumb -Wno-psabi -fPIC -fvisibility=hidden -Wall -W -D_REENTRANT -fPIE\n") + fmt.Fprint(bb, "#cgo CXXFLAGS: -pipe -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -feliminate-unused-debug-types -fexceptions -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -fmessage-length=0 -march=armv7ve -mfloat-abi=softfp -mfpu=neon -mthumb -Wno-psabi -fPIC -fvisibility=hidden -Wall -W -D_REENTRANT -fPIE\n") + + fmt.Fprint(bb, "#cgo CXXFLAGS: -DQT_NO_DEBUG") + for _, m := range libs { + fmt.Fprintf(bb, " -DQT_%v_LIB", strings.ToUpper(m)) + } + fmt.Fprint(bb, "\n") + + fmt.Fprintf(bb, "#cgo CXXFLAGS: -I%[1]s/usr/include/c++/6.2.0/arm-oe-linux-gnueabi -I%[1]s/usr/include/c++/6.2.0 -I%[1]s/usr/lib/mkspecs -I%[1]s/usr/include -I%[1]s/usr/include/mdeclarativecache5 -I%[1]s/usr/include/resource/qt5\n", os.Getenv("OECORE_TARGET_SYSROOT")) + + fmt.Fprint(bb, "#cgo CXXFLAGS:") + for _, m := range libs { + fmt.Fprintf(bb, " -I%s/usr/include/Qt%v", os.Getenv("OECORE_TARGET_SYSROOT"), m) + } + fmt.Fprint(bb, "\n\n") + + fmt.Fprintf(bb, "#cgo LDFLAGS: -Wl,-O1 -Wl,-rpath-link,%[1]s/usr/lib -Wl,-rpath-link,%[1]s/lib\n", os.Getenv("OECORE_TARGET_SYSROOT")) + + fmt.Fprintf(bb, "#cgo LDFLAGS: -rdynamic -L%[1]s/usr/lib -L%[1]s/lib -lmdeclarativecache5", os.Getenv("OECORE_TARGET_SYSROOT")) + for _, m := range libs { + if m != "UiPlugin" { + if parser.IsWhiteListedSailfishLib(m) { + fmt.Fprintf(bb, " -lQt5%v", m) + } + } + } + + fmt.Fprint(bb, " -lGLESv2 -lpthread\n") + fmt.Fprint(bb, "*/\n") + + fmt.Fprint(bb, "import \"C\"\n") + + var tmp = strings.Replace(bb.String(), "${BUILDTARGET}", "asteroid", -1) + tmp = strings.Replace(tmp, "i486", "armv7ve", -1) + + switch { + case mode == MOC: + { + utils.Save(filepath.Join(mocPath, "moc_cgo_asteroid_linux_arm.go"), tmp) + } + + case mode == MINIMAL: + { + utils.Save(utils.GoQtPkgPath(strings.ToLower(module), "minimal_cgo_asteroid_linux_arm.go"), tmp) + } + + default: + { + utils.Save(utils.GoQtPkgPath(strings.ToLower(module), "cgo_asteroid_linux_arm.go"), tmp) + } + } +} diff --git a/qt/tool-chain/binding/templater/template_cgo_qmake.go b/qt/tool-chain/binding/templater/template_cgo_qmake.go new file mode 100644 index 0000000..5298651 --- /dev/null +++ b/qt/tool-chain/binding/templater/template_cgo_qmake.go @@ -0,0 +1,707 @@ +package templater + +import ( + "bytes" + "fmt" + "go/format" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +const ( + NONE = iota + MOC + MINIMAL + RCC +) + +func CgoTemplate(module, path, target string, mode int, ipkg, tags string) (o string) { + return cgoTemplate(module, path, target, mode, ipkg, tags, parser.LibDeps[module]) +} + +func CgoTemplateSafe(module, path, target string, mode int, ipkg, tags string, libs []string) (o string) { + return cgoTemplate(module, path, target, mode, ipkg, tags, libs) +} + +func cgoTemplate(module, path, target string, mode int, ipkg, tags string, libs []string) (o string) { + utils.Log.WithField("module", module).WithField("path", path).WithField("target", target).WithField("mode", mode).WithField("pkg", ipkg).Debug("running cgoTemplate") + + switch module { + case "AndroidExtras": + if !(target == "android" || target == "android-emulator") { + return + } + case "Sailfish": + if !strings.HasPrefix(target, "sailfish") { + return + } + } + + if path == "" { + path = utils.GoQtPkgPath(strings.ToLower(module)) + } + + //TODO: differentiate between docker and virtual-box build for sailfish targets + if !(target == "sailfish" || target == "sailfish-emulator" || target == "js" || target == "wasm") { + if !(parser.ShouldBuildForTarget(module, target) || mode == MOC || mode == RCC) || + isAlreadyCached(module, path, target, mode, libs) { + utils.Log.Debugf("skipping cgo generation") + return + } + } + + switch target { + case "sailfish", "sailfish-emulator": + cgoSailfish(module, path, mode, ipkg, libs) //TODO: + case "asteroid": + cgoAsteroid(module, path, mode, ipkg) //TODO: + default: + createProject(module, path, target, mode, libs) + createMakefile(module, path, target, mode) + o = createCgo(module, path, target, mode, ipkg, tags) + } + + utils.RemoveAll(filepath.Join(path, "Mfile")) + utils.RemoveAll(filepath.Join(path, "Mfile.Release")) + + return +} + +//TODO: use qmake props ? +func isAlreadyCached(module, path, target string, mode int, libs []string) bool { + for _, file := range cgoFileNames(module, path, target, mode) { + file = filepath.Join(path, file) + if utils.ExistsFile(file) { + file = utils.Load(file) + + for _, dep := range libs { + if !strings.Contains(strings.ToLower(file), "_"+strings.ToLower(dep)+"_") { + utils.Log.Debugln("cgo does not contain:", strings.ToLower(dep)) + return false + } + } + + allLibs := parser.GetLibs() + parser.LibDepsMutex.Lock() + for i := len(allLibs) - 1; i >= 0; i-- { + for _, dep := range append(libs, module) { + var broke bool + for _, lib := range append(parser.LibDeps[dep], dep) { + if allLibs[i] == lib { + allLibs = append(allLibs[:i], allLibs[i+1:]...) + broke = true + break + } + } + if broke { + break + } + } + } + parser.LibDepsMutex.Unlock() + + for _, dep := range allLibs { + if strings.Contains(strings.ToLower(file), "_"+strings.ToLower(dep)+"_") { + utils.Log.Debugln("cgo does contain extra:", strings.ToLower(dep)) + return false + } + } + + if utils.QT_DEBUG_QML() { + if strings.Contains(file, "-DQT_NO_DEBUG") { + utils.Log.Debugln("non debug cgo file, re-creating ...") + return false + } + } else { + if strings.Contains(file, "-DQT_QML_DEBUG") || strings.Contains(file, "-DQT_DECLARATIVE_DEBUG") { + utils.Log.Debugln("non release cgo file, re-creating ...") + return false + } + } + + if !strings.Contains(file, utils.QT_VERSION()) && strings.Contains(file, "5.") { + utils.Log.Debugln("wrong cgo file qt version, re-creating ...") + return false + } + + switch target { + case "windows": + if utils.QT_DEBUG_CONSOLE() { + if strings.Contains(file, "subsystem,windows") { + utils.Log.Debugln("wrong subsystem: have windows and want console, re-creating ...") + return false + } + } else { + if strings.Contains(file, "subsystem,console") { + utils.Log.Debugln("wrong subsystem: have console and want windows, re-creating ...") + return false + } + } + case "darwin": + if !strings.Contains(file, utils.MACOS_SDK_DIR()) { + utils.Log.Debugln("wrong MACOS_SDK_DIR, re-creating ...") + return false + } + case "ios": + if !strings.Contains(file, utils.IPHONEOS_SDK_DIR()) { + utils.Log.Debugln("wrong IPHONEOS_SDK_DIR, re-creating ...") + return false + } + case "ios-simulator": + if !strings.Contains(file, utils.IPHONESIMULATOR_SDK_DIR()) { + utils.Log.Debugln("wrong IPHONESIMULATOR_SDK_DIR, re-creating ...") + return false + } + } + + containsPath := func(file, path string) bool { + r := strings.Contains(strings.Replace(strings.Replace(file, "\\", "", -1), "/", "", -1), strings.Replace(strings.Replace(strings.TrimPrefix(path, filepath.VolumeName(path)), "\\", "", -1), "/", "", -1)) + if !r { + utils.Log.Debugln("wrong qt path, re-creating ...") + } + return r + } + + switch target { + case "darwin", "linux", "windows", "ubports": + //TODO: msys pkg-config mxe brew + switch { + case utils.QT_HOMEBREW(), utils.QT_MACPORTS(), utils.QT_NIX(): + return containsPath(file, utils.QT_DARWIN_DIR()) + case utils.QT_MSYS2(): + return containsPath(file, utils.QT_MSYS2_DIR()) + default: + return containsPath(file, utils.QT_DIR()) || strings.Contains(file, utils.QT_MXE_TRIPLET()) + } + case "android", "android-emulator": + return containsPath(file, utils.QT_DIR()) && strings.Contains(file, utils.ANDROID_NDK_DIR()) + case "ios", "ios-simulator": + return containsPath(file, utils.QT_DIR()) || strings.Contains(file, utils.QT_DARWIN_DIR()) + case "sailfish", "sailfish-emulator", "asteroid": + case "rpi1", "rpi2", "rpi3": + return containsPath(file, strings.TrimSpace(utils.RunCmd(exec.Command(utils.ToolPath("qmake", target), "-query", "QT_INSTALL_LIBS"), fmt.Sprintf("query lib path for %v on %v", target, runtime.GOOS)))) + case "js", "wasm": + } + } + } + return false +} + +func createProject(module, path, target string, mode int, libs []string) { + var out []string + + switch { + case mode == RCC: + out = []string{"Core"} + case mode == MOC, module == "build_static": + out = libs + case mode == MINIMAL, mode == NONE: + out = append([]string{module}, libs...) + } + + for i, v := range out { + if v == "Speech" { + out[i] = "TextToSpeech" + } + out[i] = strings.ToLower(out[i]) + } + + proPath := filepath.Join(path, "..", fmt.Sprintf("%v.pro", filepath.Base(path))) + if module == "build_static" { + proPath = filepath.Join(path, "..", "..", fmt.Sprintf("%v.pro", filepath.Base(path))) + } + + if utils.QT_UBPORTS() { + proPath = strings.Replace(proPath, "/../", "/", -1) + proPath = strings.Replace(proPath, "/", "_", -1) + proPath = filepath.Join("/home", "user", proPath) + } + + utils.Save(proPath, fmt.Sprintf("QT += %v", strings.Join(out, " "))) +} + +func createMakefile(module, path, target string, mode int) { + + for _, suf := range []string{"_plugin_import", "_qml_plugin_import"} { + pPath := filepath.Join(path, fmt.Sprintf("%v%v.cpp", filepath.Base(path), suf)) + if utils.ExistsFile(pPath) { + utils.RemoveAll(pPath) + } + } + + proPath := filepath.Join(path, "..", fmt.Sprintf("%v.pro", filepath.Base(path))) + if module == "build_static" { + proPath = filepath.Join(path, "..", "..", fmt.Sprintf("%v.pro", filepath.Base(path))) + } + + mPath := "Mfile" + if utils.QT_UBPORTS() { + proPath = strings.Replace(proPath, "/../", "/", -1) + proPath = strings.Replace(proPath, "/", "_", -1) + proPath = filepath.Join("/home", "user", proPath) + mPath = proPath + mPath + } + + relProPath, err := filepath.Rel(path, proPath) + if err != nil || utils.QT_UBPORTS() { + relProPath = proPath + } + env, _, _, _ := cmd.BuildEnv(target, "", "") + cmd := exec.Command(utils.ToolPath("qmake", target), "-o", mPath, relProPath) + cmd.Dir = path + switch target { + case "darwin": + cmd.Args = append(cmd.Args, []string{"-spec", "macx-clang", "CONFIG+=x86_64"}...) + case "windows": + subsystem := "windows" + if utils.QT_DEBUG_CONSOLE() { + subsystem = "console" + } + cmd.Args = append(cmd.Args, []string{"-spec", "win32-g++", "CONFIG+=" + subsystem}...) + case "linux": + cmd.Args = append(cmd.Args, []string{"-spec", "linux-g++"}...) + case "ios": + cmd.Args = append(cmd.Args, []string{"-spec", "macx-ios-clang", "CONFIG+=iphoneos", "CONFIG+=device"}...) + case "ios-simulator": + cmd.Args = append(cmd.Args, []string{"-spec", "macx-ios-clang", "CONFIG+=iphonesimulator", "CONFIG+=simulator"}...) + case "android", "android-emulator": + cmd.Args = append(cmd.Args, []string{"-spec", "android-clang"}...) + cmd.Env = []string{fmt.Sprintf("ANDROID_NDK_ROOT=%v", utils.ANDROID_NDK_DIR())} + case "sailfish", "sailfish-emulator": + cmd.Args = append(cmd.Args, []string{"-spec", "linux-g++"}...) + cmd.Env = []string{ + "MER_SSH_PORT=2222", + fmt.Sprintf("MER_SSH_PRIVATE_KEY=%v", filepath.Join(utils.SAILFISH_DIR(), "vmshare", "ssh", "private_keys", "engine", "mersdk")), + fmt.Sprintf("MER_SSH_PROJECT_PATH=%v", cmd.Dir), + fmt.Sprintf("MER_SSH_SDK_TOOLS=%v/.config/SailfishOS-SDK/mer-sdk-tools/MerSDK/SailfishOS-armv7hl", os.Getenv("HOME")), + fmt.Sprintf("MER_SSH_SHARED_HOME=%v", os.Getenv("HOME")), + fmt.Sprintf("MER_SSH_SHARED_SRC=%v", utils.MustGoPath()), + "MER_SSH_SHARED_TARGET=/opt/SailfishOS/mersdk/targets", + "MER_SSH_TARGET_NAME=SailfishOS-armv7hl", + "MER_SSH_USERNAME=mersdk", + } + case "asteroid": + case "rpi1": + cmd.Args = append(cmd.Args, []string{"-spec", "devices/linux-rasp-pi-g++"}...) + case "rpi2": + cmd.Args = append(cmd.Args, []string{"-spec", "devices/linux-rasp-pi2-g++"}...) + case "rpi3": + cmd.Args = append(cmd.Args, []string{"-spec", "devices/linux-rpi3-g++"}...) + case "ubports": + if utils.QT_UBPORTS_ARCH() == "arm" { + if utils.QT_UBPORTS_VERSION() == "vivid" { + cmd.Args = append(cmd.Args, []string{"-spec", "ubuntu-arm-gnueabihf-g++"}...) + } else { + cmd.Args = append(cmd.Args, []string{"-spec", "linux-g++"}...) + } + } else { + if utils.QT_UBPORTS_VERSION() == "vivid" { + cmd.Args = append(cmd.Args, []string{"-spec", "linux-g++-64"}...) + } else { + cmd.Args = append(cmd.Args, []string{"-spec", "linux-g++"}...) + } + } + case "js", "wasm": + cmd.Args = append(cmd.Args, []string{"-spec", "wasm-emscripten"}...) + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + } + + if utils.QT_DEBUG_QML() { + cmd.Args = append(cmd.Args, []string{"CONFIG+=debug", "CONFIG+=declarative_debug", "CONFIG+=qml_debug"}...) + } else { + cmd.Args = append(cmd.Args, "CONFIG+=release") + } + + if (target == "android" || target == "android-emulator") && runtime.GOOS == "windows" { + //TODO: use os.Setenv instead? --> + utils.SaveExec(filepath.Join(cmd.Dir, "qmake.bat"), fmt.Sprintf("set ANDROID_NDK_ROOT=%v\r\nset ANDROID_NDK_HOST=windows-x86_64\r\n%v", utils.ANDROID_NDK_DIR(), strings.Join(cmd.Args, " "))) + cmd = exec.Command(".\\qmake.bat") + cmd.Dir = path + utils.RunCmdOptional(cmd, fmt.Sprintf("run qmake for %v on %v", target, runtime.GOOS)) + utils.RemoveAll(filepath.Join(cmd.Dir, "qmake.bat")) + //<-- + } else { + utils.RunCmdOptional(cmd, fmt.Sprintf("run qmake for %v on %v", target, runtime.GOOS)) + } + + if utils.QT_UBPORTS() { + utils.Save(filepath.Join(path, "Mfile"), utils.Load(mPath)) + utils.RemoveAll(mPath) + } + + utils.RemoveAll(proPath) + utils.RemoveAll(filepath.Join(path, ".qmake.stash")) + switch target { + case "darwin": + case "windows": + for _, suf := range []string{"_plugin_import", "_qml_plugin_import"} { + pPath := filepath.Join(path, fmt.Sprintf("%v%v.cpp", filepath.Base(path), suf)) + if (utils.QT_MXE_STATIC() || utils.QT_MSYS2_STATIC()) && utils.ExistsFile(pPath) { + if content := utils.Load(pPath); !strings.Contains(content, "+build windows") { + utils.Save(pPath, "// +build windows\r\n"+content) + } + } + if mode == MOC || mode == RCC || !(utils.QT_MXE_STATIC() || utils.QT_MSYS2_STATIC()) || (!strings.HasPrefix(module, "Q") && strings.Contains(pPath, "_qml_")) { + utils.RemoveAll(pPath) + } + } + for _, n := range []string{"Mfile", "Mfile.Debug", "release", "debug"} { + utils.RemoveAll(filepath.Join(path, n)) + } + case "linux": + case "ios", "ios-simulator": + for _, suf := range []string{"_plugin_import", "_qml_plugin_import"} { + pPath := filepath.Join(path, fmt.Sprintf("%v%v.cpp", filepath.Base(path), suf)) + /* TODO when shared builds are available: + if utils.QT_VERSION_MAJOR() == "5.9" && utils.ExistsFile(pPath) { + if content := utils.Load(pPath); !strings.Contains(content, "+build ios,!darwin") { + utils.Save(pPath, "// +build ios,!darwin\n"+utils.Load(pPath)) + } + } + */ + if module != "build_static" /*TODO when shared builds are available: utils.QT_VERSION_MAJOR() != "5.9"*/ || mode == MOC || mode == RCC { + utils.RemoveAll(pPath) + } + } + for _, n := range []string{"Info.plist", "qt.conf"} { + utils.RemoveAll(filepath.Join(path, n)) + } + utils.RemoveAll(filepath.Join(path, fmt.Sprintf("%v.xcodeproj", filepath.Base(path)))) + case "android", "android-emulator": + utils.RemoveAll(filepath.Join(path, fmt.Sprintf("android-lib%v.so-deployment-settings.json", filepath.Base(path)))) + case "sailfish", "sailfish-emulator": + case "asteroid": + case "rpi1", "rpi2", "rpi3": + case "ubports": + case "js", "wasm": + for _, suf := range []string{".js_plugin_import", ".js_qml_plugin_import"} { + pPath := filepath.Join(path, fmt.Sprintf("%v%v.cpp", filepath.Base(path), suf)) + if module != "build_static" || mode == MOC || mode == RCC { + utils.RemoveAll(pPath) + } + } + } +} + +func createCgo(module, path, target string, mode int, ipkg, tags string) string { + bb := new(bytes.Buffer) + defer bb.Reset() + + if mode == MOC && tags != "" { + bb.WriteString("// +build " + tags + "\n") + } + + guards := "// +build " + switch target { + case "darwin": + guards += "!ios" + case "android", "android-emulator": + guards += strings.Replace(target, "-", "_", -1) + case "ios", "ios-simulator": + guards += "ios" + case "sailfish", "sailfish-emulator": + guards += strings.Replace(target, "-", "_", -1) + case "asteroid": + guards += target + case "rpi1", "rpi2", "rpi3": + guards += target + case "js", "wasm": + guards += "ignore" + } + //TODO: move "minimal" build tag in separate line --> + switch mode { + case NONE: + if len(guards) > 10 { + guards += "," + } + guards += "!minimal" + case MINIMAL: + if len(guards) > 10 { + guards += "," + } + guards += "minimal" + } + if len(guards) > 10 { + bb.WriteString(guards + "\n\n") + } + //<-- + + pkg := strings.ToLower(module) + if mode == MOC || pkg == "build_static" { + pkg = ipkg + } + fmt.Fprintf(bb, "package %v\n\n/*\n", pkg) + + // + + file := "Mfile" + if target == "windows" { + file += ".Release" + } + var content string + if utils.ExistsFile(filepath.Join(path, file)) { + content = utils.Load(filepath.Join(path, file)) + + for _, l := range strings.Split(content, "\n") { + switch { + case strings.HasPrefix(l, "CFLAGS"): + fmt.Fprintf(bb, "#cgo CFLAGS: %v\n", strings.Split(l, " = ")[1]) + case strings.HasPrefix(l, "CXXFLAGS"), strings.HasPrefix(l, "INCPATH"): + fmt.Fprintf(bb, "#cgo CXXFLAGS: %v\n", strings.Split(l, " = ")[1]) + case strings.HasPrefix(l, "LFLAGS"), strings.HasPrefix(l, "LIBS"): + if target == "windows" && !(utils.QT_MXE_STATIC() || utils.QT_MSYS2_STATIC()) { + pFix := []string{ + filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "mingw49_32"), + filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "mingw53_32"), + filepath.Join(utils.QT_DIR(), utils.QT_VERSION(), "mingw73_64"), + filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "mingw49_32"), + filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "mingw53_32"), + filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "mingw73_64"), + filepath.Join(utils.QT_MXE_DIR(), "usr", utils.QT_MXE_TRIPLET(), "qt5"), + utils.QT_MSYS2_DIR(), + } + for _, pFix := range pFix { + pFix = strings.Replace(filepath.Join(pFix, "lib", "lib"), "\\", "/", -1) + if strings.Contains(l, pFix) { + var cleaned []string + for _, s := range strings.Split(l, " ") { + if strings.HasPrefix(s, pFix) && (strings.HasSuffix(s, ".a") || strings.HasSuffix(s, ".dll")) { + s = strings.Replace(s, pFix, "-l", -1) + s = strings.TrimSuffix(s, ".a") + s = strings.TrimSuffix(s, ".dll") + } + cleaned = append(cleaned, s) + } + l = strings.Join(cleaned, " ") + } + } + } + fmt.Fprintf(bb, "#cgo LDFLAGS: %v\n", strings.Split(l, " = ")[1]) + } + } + } + + switch target { + case "android", "android-emulator": + fmt.Fprint(bb, "#cgo LDFLAGS: -Wl,--allow-shlib-undefined\n") + case "windows": + fmt.Fprint(bb, "#cgo LDFLAGS: -Wl,--allow-multiple-definition\n") + case "ios": + fmt.Fprintf(bb, "#cgo CXXFLAGS: -isysroot %v/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/%v -miphoneos-version-min=10.0\n", utils.XCODE_DIR(), utils.IPHONEOS_SDK_DIR()) + fmt.Fprintf(bb, "#cgo LDFLAGS: -Wl,-syslibroot,%v/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/%v -miphoneos-version-min=10.0\n", utils.XCODE_DIR(), utils.IPHONEOS_SDK_DIR()) + case "ios-simulator": + fmt.Fprintf(bb, "#cgo CXXFLAGS: -isysroot %v/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/%v -mios-simulator-version-min=10.0\n", utils.XCODE_DIR(), utils.IPHONESIMULATOR_SDK_DIR()) + fmt.Fprintf(bb, "#cgo LDFLAGS: -Wl,-syslibroot,%v/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/%v -mios-simulator-version-min=10.0\n", utils.XCODE_DIR(), utils.IPHONESIMULATOR_SDK_DIR()) + case "js", "wasm": + fmt.Fprint(bb, "#cgo CFLAGS: -s EXTRA_EXPORTED_RUNTIME_METHODS=['getValue','setValue']\n") + } + + fmt.Fprint(bb, "#cgo CFLAGS: -Wno-unused-parameter -Wno-unused-variable -Wno-return-type\n") + fmt.Fprint(bb, "#cgo CXXFLAGS: -Wno-unused-parameter -Wno-unused-variable -Wno-return-type\n") + + fmt.Fprint(bb, "*/\nimport \"C\"\n") + + out, err := format.Source(bb.Bytes()) + if err != nil { + utils.Log.WithError(err).Panicln("failed to format:", module) + } + + tmp := string(out) + + switch target { + case "darwin": + tmp = strings.Replace(tmp, "$(EXPORT_ARCH_ARGS)", "-arch x86_64", -1) + case "ios": + tmp = strings.Replace(tmp, "$(EXPORT_ARCH_ARGS)", "-arch arm64", -1) + tmp = strings.Replace(tmp, "$(EXPORT_QMAKE_XARCH_CFLAGS)", "", -1) + tmp = strings.Replace(tmp, "$(EXPORT_QMAKE_XARCH_LFLAGS)", "", -1) + case "ios-simulator": + tmp = strings.Replace(tmp, "$(EXPORT_ARCH_ARGS)", "-arch x86_64", -1) + tmp = strings.Replace(tmp, "$(EXPORT_QMAKE_XARCH_CFLAGS)", "", -1) + tmp = strings.Replace(tmp, "$(EXPORT_QMAKE_XARCH_LFLAGS)", "", -1) + case "android", "android-emulator": //TODO: + tmp = strings.Replace(tmp, fmt.Sprintf("-Wl,-soname,lib%v.so", filepath.Base(path)), "-Wl,-soname,libgo_base.so", -1) + tmp = strings.Replace(tmp, "-shared", "", -1) + case "js", "wasm": + tmp = strings.Replace(tmp, "\"", "", -1) + if utils.QT_DEBUG() { + tmp = strings.Replace(tmp, "-s USE_FREETYPE=1", "-s USE_FREETYPE=1 -s ASSERTIONS=1", -1) + } + } + + for _, variable := range []string{"DEFINES", "SUBLIBS", "EXPORT_QMAKE_XARCH_CFLAGS", "EXPORT_QMAKE_XARCH_LFLAGS", "EXPORT_ARCH_ARGS", "-fvisibility=hidden", "-fembed-bitcode"} { + for _, l := range strings.Split(content, "\n") { + if strings.HasPrefix(l, variable+" ") { + if strings.Contains(l, "-DQT_TESTCASE_BUILDDIR") { + l = strings.Split(l, "-DQT_TESTCASE_BUILDDIR")[0] + } + tmp = strings.Replace(tmp, fmt.Sprintf("$(%v)", variable), strings.Split(l, " = ")[1], -1) + } + } + tmp = strings.Replace(tmp, fmt.Sprintf("$(%v)", variable), "", -1) + tmp = strings.Replace(tmp, variable, "", -1) + } + tmp = strings.Replace(tmp, "\\", "/", -1) + + if module == "build_static" { + return tmp + } + + for _, file := range cgoFileNames(module, path, target, mode) { + switch target { + case "android", "android-emulator": + tmp = strings.Replace(tmp, "/opt/android/"+filepath.Base(utils.ANDROID_NDK_DIR()), utils.ANDROID_NDK_DIR(), -1) + case "darwin": + for _, lib := range []string{"WebKitWidgets", "WebKit"} { + tmp = strings.Replace(tmp, "-lQt5"+lib, "-framework Qt"+lib, -1) + } + tmp = strings.Replace(tmp, "-Wl,-rpath,@executable_path/Frameworks", "", -1) + case "windows": + if utils.QT_MSYS2() { + tmp = strings.Replace(tmp, ",--relax,--gc-sections", "", -1) + if utils.QT_MSYS2_STATIC() { + tmp = strings.Replace(tmp, "-ffunction-sections", "", -1) + tmp = strings.Replace(tmp, "-fdata-sections", "", -1) + tmp = strings.Replace(tmp, "-Wl,--gc-sections", "", -1) + } + } + if utils.QT_MSYS2() && utils.QT_MSYS2_ARCH() == "amd64" { + tmp = strings.Replace(tmp, " -Wa,-mbig-obj ", " ", -1) + } + if (utils.QT_MSYS2() && utils.QT_MSYS2_ARCH() == "amd64") || utils.QT_MXE_ARCH() == "amd64" || + (!utils.QT_MXE() && !utils.QT_MSYS2() && utils.QT_VERSION_NUM() >= 5120) { + tmp = strings.Replace(tmp, " -Wl,-s ", " ", -1) + } + if utils.QT_DEBUG_CONSOLE() { //TODO: necessary at all? + tmp = strings.Replace(tmp, "subsystem,windows", "subsystem,console", -1) + } else { + tmp = strings.Replace(tmp, "subsystem,console", "subsystem,windows", -1) + } + case "ios": + if strings.HasSuffix(file, "darwin_arm.go") { + tmp = strings.Replace(tmp, "arm64", "armv7", -1) + } + case "ios-simulator": + if strings.HasSuffix(file, "darwin_386.go") { + tmp = strings.Replace(tmp, "x86_64", "i386", -1) + } + case "js", "wasm": + if mode == RCC { + utils.Save(filepath.Join(path, strings.Replace(file, "_cgo_", "_stub_", -1)), "package "+pkg+"\n") + } + case "linux": + tmp = strings.Replace(tmp, "-Wl,-O1", "-O1", -1) + } + utils.Save(filepath.Join(path, file), tmp) + } + + return "" +} + +func cgoFileNames(module, path, target string, mode int) []string { + var pFix string + switch mode { + case RCC: + pFix = "rcc_" + case MOC: + pFix = "moc_" + case MINIMAL: + pFix = "minimal_" + } + + var sFixes []string + switch target { + case "darwin": + sFixes = []string{"darwin_amd64"} + case "linux": + sFixes = []string{"linux_" + utils.GOARCH()} + case "windows": + if utils.QT_MXE_ARCH() == "amd64" || (utils.QT_MSYS2() && utils.QT_MSYS2_ARCH() == "amd64") || + (!utils.QT_MXE() && !utils.QT_MSYS2() && utils.QT_VERSION_NUM() >= 5120) { + sFixes = []string{"windows_amd64"} + } else { + sFixes = []string{"windows_386"} + } + case "android": + sFixes = []string{"linux_arm"} + case "android-emulator": + sFixes = []string{"linux_386"} + case "ios": + sFixes = []string{"darwin_arm64"} + case "ios-simulator": + sFixes = []string{"darwin_amd64"} + case "sailfish": + sFixes = []string{"linux_arm"} + case "sailfish-emulator": + sFixes = []string{"linux_386"} + case "asteroid": + sFixes = []string{"linux_arm"} + case "rpi1", "rpi2", "rpi3": + sFixes = []string{"linux_arm"} + case "ubports": + sFixes = []string{"linux_" + utils.QT_UBPORTS_ARCH()} + case "js": + sFixes = []string{"js"} + case "wasm": + sFixes = []string{"wasm"} + } + + var o []string + for _, sFix := range sFixes { + o = append(o, fmt.Sprintf("%vcgo_%v_%v.go", pFix, strings.Replace(target, "-", "_", -1), sFix)) + } + return o +} + +func ParseCgo(module, target string) (string, string) { + utils.Log.WithField("module", module).WithField("target", target).Debug("parse cgo for shared lib") + + //TODO: use "go list" instead + + tmp := utils.LoadOptional(utils.GoQtPkgPath(module, cgoFileNames(module, "", target, NONE)[0])) + if tmp != "" { + tmp = strings.Split(tmp, "/*")[1] + tmp = strings.Split(tmp, "*/")[0] + + tmp = strings.Replace(tmp, "#cgo CFLAGS: ", "", -1) + tmp = strings.Replace(tmp, "#cgo CXXFLAGS: ", "", -1) + tmp = strings.Replace(tmp, "#cgo LDFLAGS: ", "", -1) + tmp = strings.Replace(tmp, "\n", " ", -1) + + switch target { + case "darwin": + return "clang++", fmt.Sprintf("%v -Wl,-S -Wl,-x -install_name @rpath/%[2]v/lib%[2]v.so -undefined dynamic_lookup -shared -o lib%[2]v.so %[2]v.cpp", tmp, module) + case "js", "wasm": + env, _, _, _ := cmd.BuildEnv(target, "", "") + return filepath.Join(env["EMSCRIPTEN"], "em++"), fmt.Sprintf("%v -o %[2]v.o %[2]v.js_plugin_import.cpp %[2]v.cpp", tmp, module) + } + } + + return "", tmp +} + +func ReplaceCgo(module, target string) { + utils.Log.WithField("module", module).WithField("target", target).Debug("replace cgo for shared lib") + + if target == "js" || target == "wasm" { + //TODO: cleanup ? + //utils.RemoveAll(utils.GoQtPkgPath(module, cgoFileNames(module, "", target, NONE)[0])) + return + } + + tmp := utils.LoadOptional(utils.GoQtPkgPath(module, cgoFileNames(module, "", target, NONE)[0])) + if tmp != "" { + pre := strings.Split(tmp, "/*")[0] + past := strings.Split(tmp, "*/")[1] + utils.Save(utils.GoQtPkgPath(module, cgoFileNames(module, "", target, NONE)[0]), fmt.Sprintf("%v/*\n#cgo CFLAGS: -I.\n#cgo LDFLAGS: -L. -l%v -Wl,-rpath,%v\n*/%v", pre, module, utils.GoQtPkgPath(), past)) + } +} diff --git a/qt/tool-chain/binding/templater/template_cpp.go b/qt/tool-chain/binding/templater/template_cpp.go new file mode 100644 index 0000000..f65e939 --- /dev/null +++ b/qt/tool-chain/binding/templater/template_cpp.go @@ -0,0 +1,811 @@ +package templater + +import ( + "bytes" + "crypto/sha1" + "encoding/hex" + "fmt" + "math" + "math/rand" + "sort" + "strings" + "time" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +var exportedFunctions []string +var CleanupDepsForCI = func() {} + +func CppTemplate(module string, mode int, target, tags string) []byte { + utils.Log.WithField("module", module).Debug("generating cpp") + exportedFunctions = make([]string, 0) + parser.State.Target = target + + var bb = new(bytes.Buffer) + defer bb.Reset() + + if mode != MOC { + module = "Qt" + module + } else { + for _, c := range parser.SortedClassNamesForModule(module, true) { + var class, e = parser.State.ClassMap[c] + if !e { + continue + } + + var typeMap = make(map[string]string) + for _, f := range class.Functions { + if parser.IsPackedMap(f.Output) { + var tHash = sha1.New() + tHash.Write([]byte(f.Output)) + typeMap[f.Output] = hex.EncodeToString(tHash.Sum(nil)[:3]) + } + for _, p := range f.Parameters { + if parser.IsPackedMap(p.Value) { + var tHash = sha1.New() + tHash.Write([]byte(p.Value)) + typeMap[p.Value] = hex.EncodeToString(tHash.Sum(nil)[:3]) + } + } + } + + for _, p := range class.Properties { + if parser.IsPackedMap(p.Output) { + var tHash = sha1.New() + tHash.Write([]byte(p.Output)) + typeMap[p.Output] = hex.EncodeToString(tHash.Sum(nil)[:3]) + } + } + + for typ, hash := range typeMap { + fmt.Fprintf(bb, "typedef %v type%v;\n", typ, hash) + } + } + } + + if module == "QtCharts" || module == "QtDataVisualization" { + for _, classname := range parser.SortedClassNamesForModule(module, true) { + fmt.Fprintf(bb, "typedef %v::%v %v;\n", module, classname, classname) + } + fmt.Fprint(bb, "\n") + } + + for _, className := range parser.SortedClassNamesForModule(module, true) { + var class = parser.State.ClassMap[className] + + if class.IsSupported() { + + if class.HasCallbackFunctions() || mode == MOC { + + //TODO: split + fmt.Fprintf(bb, + `class %v%v: public %v +{ +%vpublic: +`, + func() string { + if mode == MOC { + return "" + } + return "My" + }(), + + class.Name, + + func() string { + if mode == MOC { + return class.GetBases()[0] + } + return class.Name + }(), + + func() string { + if mode == MOC { + bb := new(bytes.Buffer) + defer bb.Reset() + fmt.Fprintln(bb, "Q_OBJECT") + + for _, p := range class.Properties { + + ty := p.Output + if parser.IsPackedMap(p.Output) { + var tHash = sha1.New() + tHash.Write([]byte(p.Output)) + ty = fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])) + } + + fmt.Fprintf(bb, "Q_PROPERTY(%v PREPRO%v READ %v WRITE set%v NOTIFY %vChanged)\n", ty, p.Name, + func() string { + if p.Output == "bool" && !strings.HasPrefix(strings.ToLower(p.Name), "is") { + return "is" + strings.Title(p.Name) + } + return p.Name + }(), strings.Title(p.Name), p.Name) + } + + return bb.String() + } + return "" + }()) + + if !hasUnimplementedPureVirtualFunctions(class.Name) { + for _, function := range class.Functions { + if function.Meta != parser.CONSTRUCTOR || !function.IsSupported() { + continue + } + + out := fmt.Sprintf("\t%v%v(%v) : %v(%v) {%v};\n", + func() string { + if mode == MOC { + return "" + } + return "My" + }(), + + function.ClassName(), + + func() string { + var input []string + for _, p := range function.OgParameters { + name := parser.CleanName(p.Name, p.Value) + if p.Default != "" { + if strings.HasSuffix(p.Value, "*") || strings.HasSuffix(p.Value, "&") { + input = append(input, p.Value+name+" = "+p.Default) + } else { + input = append(input, p.Value+" "+name+" = "+p.Default) + } + } else { + if strings.HasSuffix(p.Value, "*") || strings.HasSuffix(p.Value, "&") { + input = append(input, p.Value+name) + } else { + input = append(input, p.Value+" "+name) + } + } + } + return strings.Join(input, ", ") + }(), + + func() string { + if mode == MOC { + return class.GetBases()[0] + } + return function.ClassName() + }(), + + func() string { + input := make([]string, len(function.Parameters)) + for i, p := range function.Parameters { + input[i] = parser.CleanName(p.Name, p.Value) + } + return strings.Join(input, ", ") + }(), + + func() string { + var pre string + if class.IsSubClassOfQObject() { + pre = fmt.Sprintf("%[1]v_%[1]v_QRegisterMetaType();", className) + } + if mode != MOC { + return pre + } + if UseJs() { + return fmt.Sprintf("qRegisterMetaType(\"quintptr\");%[1]v%[2]v_%[2]v_QRegisterMetaTypes();emscripten::val::global(\"Module\").call(\"_callback%[2]v_Constructor\", reinterpret_cast(this));", pre, className) + } + return fmt.Sprintf("qRegisterMetaType(\"quintptr\");%[1]v%[2]v_%[2]v_QRegisterMetaTypes();callback%[2]v_Constructor(this);", pre, className) + }(), + ) + + fmt.Fprint(bb, out) + } + } + + //callback functions + implementedVirtuals := make(map[string]struct{}) + for i, parentClassName := range append([]string{class.Name}, class.GetAllBases()...) { + var parentClass, e = parser.State.ClassMap[parentClassName] + if !e || !parentClass.IsSupported() { + continue + } + + for _, f := range parentClass.Functions { + var _, e = implementedVirtuals[f.Name+f.OverloadNumber] + if e || !f.IsSupported() { + continue + } + + if (parentClass.Module == parser.MOC || parentClass.Pkg != "") && f.Meta == parser.SLOT { + continue + } + + if i > 0 && (f.Meta == parser.CONSTRUCTOR || f.Meta == parser.DESTRUCTOR) { + continue + } + + implementedVirtuals[f.Name+f.OverloadNumber] = struct{}{} + + var f = *f + f.SignalMode = parser.CALLBACK + f.Fullname = fmt.Sprintf("%v::%v", class.Name, f.Name) + f.Fullname = fmt.Sprintf("%v::%v", f.FindDeepestImplementation(), f.Name) + + if f.Meta == parser.SLOT || f.Meta == parser.SIGNAL || f.Virtual == parser.IMPURE || f.Virtual == parser.PURE { + if fb := cppFunctionCallback(&f); len(fb) != 0 { + fmt.Fprintf(bb, "\t%v\n", fb) + } + } + } + } + + if mode == MOC { + for _, p := range class.Properties { + + var ty = p.Output + if parser.IsPackedMap(p.Output) { + var tHash = sha1.New() + tHash.Write([]byte(p.Output)) + ty = fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])) + } + + fmt.Fprintf(bb, "\t%v %v%v() { return _%v; };\n", + ty, + func() string { + if p.Output == "bool" && !strings.HasPrefix(strings.ToLower(p.Name), "is") { + return "is" + strings.Title(p.Name) + } + return p.Name + }(), + func() string { + if p.IsMocSynthetic { + return "" + } + return "Default" + }(), + p.Name, + ) + fmt.Fprintf(bb, "\tvoid set%v%v(%v p) { if (p != _%v) { _%v = p; %vChanged(_%v); } };\n", + strings.Title(p.Name), + func() string { + if p.IsMocSynthetic { + return "" + } + return "Default" + }(), + ty, + p.Name, + p.Name, + p.Name, + p.Name, + ) + } + + fmt.Fprintln(bb, "signals:") + for _, function := range class.Functions { + if function.Meta == parser.SIGNAL { + var function = *function + function.Meta = parser.SLOT + if fb := cppFunctionCallbackHeader(&function); len(fb) != 0 { + fmt.Fprintf(bb, "\t%v;\n", fb) + } + } + } + + fmt.Fprintln(bb, "public slots:") + for _, function := range class.Functions { + if function.Meta == parser.SLOT { + if fb := cppFunctionCallback(function); len(fb) != 0 { + fmt.Fprintf(bb, "\t%v\n", fb) + } + } + } + + fmt.Fprintln(bb, "private:") + for _, p := range class.Properties { + var ty = p.Output + if parser.IsPackedMap(p.Output) { + var tHash = sha1.New() + tHash.Write([]byte(p.Output)) + ty = fmt.Sprintf("type%v", hex.EncodeToString(tHash.Sum(nil)[:3])) + } + + fmt.Fprintf(bb, "\t%v _%v;\n", ty, p.Name) + } + } + + fmt.Fprint(bb, "};\n\n") + } + if class.IsSubClassOfQObject() { + fmt.Fprintf(bb, "Q_DECLARE_METATYPE(%v%v*)\n\n", + func() string { + if mode != MOC { + return "My" + } + return "" + }(), class.Name) + + if mode != MOC { + if strings.HasPrefix(class.Name, "QMac") && !strings.HasPrefix(parser.State.ClassMap[class.Name].Module, "QtMac") { + fmt.Fprintf(bb, "int %[1]v_%[1]v_QRegisterMetaType(){\n\t#ifdef Q_OS_OSX\n\t\tqRegisterMetaType<%[1]v*>(); return qRegisterMetaType();\n\t#else\n\t\treturn 0;\n\t#endif\n}\n\n", class.Name) + } else { + fmt.Fprintf(bb, "int %[1]v_%[1]v_QRegisterMetaType(){qRegisterMetaType<%[1]v*>(); return qRegisterMetaType();}\n\n", class.Name) + } + } else { + var typeMap = make(map[string]string) + for _, f := range class.Functions { + if parser.IsPackedMap(f.Output) { + var tHash = sha1.New() + tHash.Write([]byte(f.Output)) + typeMap[f.Output] = hex.EncodeToString(tHash.Sum(nil)[:3]) + } + for _, p := range f.Parameters { + if parser.IsPackedMap(p.Value) { + var tHash = sha1.New() + tHash.Write([]byte(p.Value)) + typeMap[p.Value] = hex.EncodeToString(tHash.Sum(nil)[:3]) + } + } + } + + propTypes := make(map[string]struct{}) + for _, p := range class.Properties { + if parser.IsPackedMap(p.Output) { + var tHash = sha1.New() + tHash.Write([]byte(p.Output)) + typeMap[p.Output] = hex.EncodeToString(tHash.Sum(nil)[:3]) + } + if o := converter.CppRegisterMetaTypeProp(p); o != "" { + propTypes[o] = struct{}{} + } + } + + for _, hash := range typeMap { + if hash == "30021d" || //QHash + hash == "95ad14" || //QHash + hash == "d01680" || //QHash + hash == "d15f9e" || //QMap + hash == "cc064b" || //QMap + hash == "378cdd" { //QMap + continue + } + fmt.Fprintf(bb, "Q_DECLARE_METATYPE(type%v)\n", hash) + } + + fmt.Fprintf(bb, "\nvoid %[1]v_%[1]v_QRegisterMetaTypes() {\n", class.Name) + for _, hash := range typeMap { + fmt.Fprintf(bb, "\tqRegisterMetaType(\"type%v\");\n", hash, hash) + } + for t := range propTypes { + fmt.Fprintf(bb, "\tqRegisterMetaType<%v>();\n", t) + } + fmt.Fprint(bb, "}\n\n") + } + } + } + + if mode != MOC { + cTemplate(bb, class, cppEnum, cppFunction, "\n\n", false) + } + } + + if mode == MOC { + for _, className := range parser.SortedClassNamesForModule(module, true) { + var class = parser.State.ClassMap[className] + + if class.IsSupported() { + cTemplate(bb, class, cppEnum, cppFunction, "\n\n", false) + } + } + + if !UseJs() { + fmt.Fprintln(bb, "#include \"moc_moc.h\"") + } + } + + if UseJs() { + for _, df := range deferredFunctions { + bb.WriteString(df) + } + deferredFunctions = nil + + rand.Seed(time.Now().UTC().UnixNano()) + fmt.Fprintf(bb, "EMSCRIPTEN_BINDINGS(r%v) {\n", rand.Intn(math.MaxInt32)) //TODO: use deterministic hash instead + + sort.Stable(sort.StringSlice(exportedFunctions)) + + for _, f := range exportedFunctions { + if strings.Contains(bb.String(), f+"(") && !strings.Contains(bb.String(), "_KEEPALIVE_"+f+"(") && !strings.Contains(bb.String(), "_"+f+"\"") { + fmt.Fprintf(bb, "\temscripten::function(\"_%[1]v\", &%[1]v);\n", f) + } + + if strings.Contains(bb.String(), f+"Default(") && !strings.Contains(bb.String(), "_KEEPALIVE_"+f+"Default(") && !strings.Contains(bb.String(), "_"+f+"Default\"") { + fmt.Fprintf(bb, "\temscripten::function(\"_%[1]vDefault\", &%[1]vDefault);\n", f) + } + } + + fmt.Fprintln(bb, "}\n") + + if mode == MOC { + fmt.Fprintln(bb, "#include \"moc_moc.h\"") + } + } + + return preambleCpp(module, bb.Bytes(), mode, target, tags) +} + +func preambleCpp(module string, input []byte, mode int, target, tags string) []byte { + var bb = new(bytes.Buffer) + defer bb.Reset() + + if mode == MOC { + libsm := make(map[string]struct{}, 0) + for _, c := range parser.State.ClassMap { + if c.Pkg != "" && c.IsSubClassOfQObject() { + libsm[c.Module] = struct{}{} + } + } + + var libs []string + for k := range libsm { + libs = append(libs, k) + } + + for _, c := range parser.SortedClassesForModule(strings.Join(libs, ","), true) { + if c.Pkg == "" || !strings.Contains(string(input), c.Name) /*|| !c.HasConstructor()*/ { + continue + } + + fmt.Fprintf(bb, "class %v: public %v{\npublic:\n", c.Name, c.GetBases()[0]) + + for _, function := range c.Functions { + if function.Meta != parser.CONSTRUCTOR || !function.IsSupported() { + continue + } + + var input = make([]string, len(function.Parameters)) + for i, p := range function.Parameters { + input[i] = p.Name + } + + fmt.Fprintf(bb, "\t%v%v(%v) : %v(%v) {};\n", + func() string { + if mode == MOC { + return "" + } + return "My" + }(), + + function.ClassName(), + + strings.Split(strings.Split(function.Signature, "(")[1], ")")[0], + + func() string { + if mode == MOC { + return c.GetBases()[0] + } + return function.ClassName() + }(), + + strings.Join(input, ", "), + ) + } + fmt.Fprint(bb, "\n};\n") + } + + fmt.Fprint(bb, "\n") + + bb.Write(input) + input = []byte(bb.String()) + bb.Reset() + } + + fmt.Fprintf(bb, `%v + +#define protected public +#define private public + +#include "%v.h" +%v + +`, + buildTags(module, false, mode, tags), + + func() string { + switch module { + case "QtAndroidExtras": + { + return fmt.Sprintf("%v_android", goModule(module)) + } + + case "QtSailfish": + { + return fmt.Sprintf("%v_sailfish", goModule(module)) + } + + default: + { + if mode == MINIMAL { + return fmt.Sprintf("%v-minimal", goModule(module)) + } + + if mode == MOC { + return "moc" + } + + return goModule(module) + } + } + }(), + + func() string { + if UseJs() { + return "\n#include \n#include \n#include \n#include " + } + switch module { + case "QtAndroidExtras", "QtSailfish": + return "#include \"_cgo_export.h\"" + default: + if utils.QT_DYNAMIC_SETUP() { + return "#include \"_obj/_cgo_export.h\"" + } + return "#include \"_cgo_export.h\"" + } + }(), + ) + + var classes = make([]string, 0) + for _, class := range parser.State.ClassMap { + if (strings.Contains(string(input), class.Name+";") || + strings.Contains(string(input), class.Name+":") || + strings.Contains(string(input), class.Name+"*") || + strings.Contains(string(input), class.Name+" ") || + strings.Contains(string(input), class.Name+"<") || + strings.Contains(string(input), class.Name+">") || + strings.Contains(string(input), class.Name+"(") || + strings.Contains(string(input), class.Name+")") || + strings.Contains(string(input), class.Name+"_")) && class.Module != parser.MOC { + classes = append(classes, class.Name) + } + } + sort.Stable(sort.StringSlice(classes)) + + for _, class := range classes { + if class == "SailfishApp" { + fmt.Fprint(bb, "#include \n") + } else { + var c, _ = parser.State.ClassMap[class] + if strings.HasPrefix(c.Module, "custom_") { + continue + } + switch c.Name { + case + "Qt", + "QPdf", + "QDBus", + "QAudio", + "QMultimedia", + "QSsl", + "QPrint", + "QScript", + "QSql", + "QTest", + "QWebSocketProtocol", + "OSXBluetooth", + "QBluetooth", + "PaintContext", + "QPlatformGraphicsBuffer", + "QDBusPendingReplyTypes", + "QRemoteObjectPackets", + "QRemoteObjectStringLiterals", + "ui", + "QStringList", + "QtDwmApiDll", + "content", + "QStringView": + { + continue + } + } + + if utils.QT_VERSION_NUM() <= 5042 { + switch c.Name { + case + "QQmlAbstractProfilerAdapter", + "QQuickAsyncImageProvider", + "QQuickImageResponse": + { + continue + } + } + } + + if strings.HasPrefix(parser.State.Target, "sailfish") { + if !parser.IsWhiteListedSailfishLib(strings.TrimPrefix(c.Module, "Qt")) { + continue + } + } + + if strings.HasPrefix(parser.State.Target, "rpi") && utils.QT_RPI() { + if !parser.IsWhiteListedRaspberryLib(strings.TrimPrefix(c.Module, "Qt")) { + continue + } + } + + if c, ok := parser.State.ClassMap[class]; ok { + if strings.Contains(c.Pkg, "/vendor/") { + continue + } + } + + fmt.Fprintf(bb, "#include <%v>\n", class) + + if (strings.HasPrefix(target, "ios") || target == "js" || target == "wasm") && mode == MINIMAL { + oldModuleGo := strings.TrimPrefix(c.Module, "Qt") + + var containsSelf bool + for _, l := range parser.LibDeps["build_static"] { + if l == oldModuleGo { + containsSelf = true + break + } + } + + if !containsSelf { + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], oldModuleGo) + + switch oldModuleGo { + case "Multimedia": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "MultimediaWidgets") + case "Quick": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "QuickWidgets") + } + } + } + + if mode == MOC { + var found bool + parser.LibDepsMutex.Lock() + for _, m := range parser.LibDeps[parser.MOC] { + if m == strings.TrimPrefix(c.Module, "Qt") { + found = true + break + } + } + if !found { + parser.LibDeps[parser.MOC] = append(parser.LibDeps[parser.MOC], strings.TrimPrefix(c.Module, "Qt")) + } + parser.LibDepsMutex.Unlock() + + if target == "js" || target == "wasm" { + + found = false + for _, m := range parser.LibDeps["build_static"] { + if m == strings.TrimPrefix(c.Module, "Qt") { + found = true + break + } + } + if !found { + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], strings.TrimPrefix(c.Module, "Qt")) + + switch strings.TrimPrefix(c.Module, "Qt") { + case "Multimedia": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "MultimediaWidgets") + case "Quick": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "QuickWidgets") + } + } + } + } + } + + c, ok := parser.State.ClassMap[class] + if ok && !strings.Contains(strings.Join(parser.LibDeps[strings.TrimPrefix(module, "Qt")], " "), strings.TrimPrefix(c.Module, "Qt")) { + if strings.HasPrefix(c.Module, "custom_") { + continue + } + + utils.Log.Debugf("%v add dependency: %v", module, c.Module) + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = append(parser.LibDeps[strings.TrimPrefix(module, "Qt")], strings.TrimPrefix(c.Module, "Qt")) + old := CleanupDepsForCI + CleanupDepsForCI = func() { + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = parser.LibDeps[strings.TrimPrefix(module, "Qt")][:len(parser.LibDeps[strings.TrimPrefix(module, "Qt")])-1] + old() + } + switch c.Module { + case "QtMultimedia": + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = append(parser.LibDeps[strings.TrimPrefix(module, "Qt")], "MultimediaWidgets") + old := CleanupDepsForCI + CleanupDepsForCI = func() { + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = parser.LibDeps[strings.TrimPrefix(module, "Qt")][:len(parser.LibDeps[strings.TrimPrefix(module, "Qt")])-1] + old() + } + case "QtWebEngine": + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = append(parser.LibDeps[strings.TrimPrefix(module, "Qt")], "WebEngineWidgets") + old := CleanupDepsForCI + CleanupDepsForCI = func() { + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = parser.LibDeps[strings.TrimPrefix(module, "Qt")][:len(parser.LibDeps[strings.TrimPrefix(module, "Qt")])-1] + old() + } + case "QtQuick": + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = append(parser.LibDeps[strings.TrimPrefix(module, "Qt")], "QuickWidgets") + old := CleanupDepsForCI + CleanupDepsForCI = func() { + parser.LibDeps[strings.TrimPrefix(module, "Qt")] = parser.LibDeps[strings.TrimPrefix(module, "Qt")][:len(parser.LibDeps[strings.TrimPrefix(module, "Qt")])-1] + old() + } + } + } + } + + if module == "QtCore" { + if !strings.Contains(bb.String(), "QTextDocument") { + fmt.Fprint(bb, "#include \n") + } + } + + if mode == MINIMAL { + if module == "QtCore" { + fmt.Fprint(bb, "#include \n") + } else if module == "QtNetwork" { + fmt.Fprint(bb, "#include \n") + } + + if !strings.Contains(bb.String(), "QStringList") { + fmt.Fprint(bb, "#include \n") + } + } + + fmt.Fprint(bb, "\n") + + for _, class := range parser.State.ClassMap { + if class.Fullname != "" && bytes.Contains(input, []byte("<"+class.Name)) { + fmt.Fprintf(bb, "typedef %v %v;\n", class.Fullname, class.Name) + } + } + + bb.Write(input) + + //TODO: regexp + if mode == MOC { + pre := bb.String() + bb.Reset() + libsm := make(map[string]struct{}, 0) + for _, c := range parser.State.ClassMap { + if c.Pkg != "" && c.IsSubClassOfQObject() { + libsm[c.Module] = struct{}{} + } + } + + var libs []string + for k := range libsm { + libs = append(libs, k) + } + libs = append(libs, module) + + for _, c := range parser.SortedClassesForModule(strings.Join(libs, ","), true) { + hName := c.Hash() + sep := []string{"\"_", "LIVE_", " ", "\t", "\n", "\r", "(", ")", ":", ";", "*", "<", ">", "&", "~", "{", "}", "[", "]", "_", "callback"} + for _, p := range sep { + for _, s := range sep { + if s == "callback" { + continue + } + pre = strings.Replace(pre, p+c.Name+s, p+c.Name+hName+s, -1) + } + } + } + pre = strings.Replace(pre, "PREPRO", "", -1) + bb.WriteString(pre) + } + + if UseJs() { + pre := bb.String() + bb.Reset() + pre = strings.Replace(pre, "_KEEPALIVE_", "", -1) + bb.WriteString(pre) + } + + return bb.Bytes() +} diff --git a/qt/tool-chain/binding/templater/template_go.go b/qt/tool-chain/binding/templater/template_go.go new file mode 100644 index 0000000..3d4f4ce --- /dev/null +++ b/qt/tool-chain/binding/templater/template_go.go @@ -0,0 +1,804 @@ +package templater + +import ( + "bytes" + "fmt" + "go/format" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func GoTemplate(module string, stub bool, mode int, pkg, target, tags string) []byte { + utils.Log.WithField("module", module).Debug("generating go") + parser.State.Target = target + + bb := new(bytes.Buffer) + defer bb.Reset() + + if mode != MOC { + module = "Qt" + module + } + + if !(UseStub(stub, module, mode) || UseJs()) { + fmt.Fprintf(bb, "func cGoUnpackString(s C.struct_%v_PackedString) string { if int(s.len) == -1 {\n return C.GoString(s.data)\n }\n return C.GoStringN(s.data, C.int(s.len)) }\n", strings.Title(module)) + fmt.Fprintf(bb, "func cGoUnpackBytes(s C.struct_%v_PackedString) []byte { if int(s.len) == -1 {\n return []byte(C.GoString(s.data))\n }\n return C.GoBytes(unsafe.Pointer(s.data), C.int(s.len)) }\n", strings.Title(module)) + } + + if UseJs() { + fmt.Fprint(bb, "func jsGoUnpackString(s string) string { dec, _ := hex.DecodeString(s)\n return string(dec)\n }\n") //TODO: calling it cGoUnpackString won't work, bug in go wasm ? + } + + if module == "QtAndroidExtras" && utils.QT_VERSION_NUM() >= 5060 { + fmt.Fprint(bb, "func QAndroidJniEnvironment_ExceptionCatch() error {\n") + if UseStub(stub, module, mode) || UseJs() { + fmt.Fprint(bb, "return nil\n") + } else { + fmt.Fprint(bb, "var err error\n") + fmt.Fprint(bb, "if QAndroidJniEnvironment_ExceptionCheck() {\n tmpExcPtr := QAndroidJniEnvironment_ExceptionOccurred()\nQAndroidJniEnvironment_ExceptionClear()\n") + fmt.Fprint(bb, "tmpExc := NewQAndroidJniObject6(tmpExcPtr)\n") + fmt.Fprint(bb, "err = errors.New(tmpExc.CallMethodString2(\"toString\", \"()Ljava/lang/String;\"))\n") + fmt.Fprint(bb, "tmpExc.DestroyQAndroidJniObject()\n") + fmt.Fprint(bb, "}\nreturn err\n") + } + fmt.Fprint(bb, "}\n\n") + + if UseStub(stub, module, mode) || UseJs() { + fmt.Fprint(bb, "func (e *QAndroidJniEnvironment) ExceptionCatch() error { return nil }\n") + } else { + fmt.Fprint(bb, "func (e *QAndroidJniEnvironment) ExceptionCatch() error { return QAndroidJniEnvironment_ExceptionCatch() }\n") + } + } + + for _, class := range parser.SortedClassesForModule(module, true) { + + class.Stub = UseStub(stub, module, mode) + + if mode != MINIMAL || (mode == MINIMAL && class.Export) { + + if mode != MOC { + fmt.Fprintf(bb, "type %v struct {\n%v\n}\n\n", + + class.Name, + + func() string { + if class.Bases == "" { + return "ptr unsafe.Pointer" + } + + var bb = new(bytes.Buffer) + defer bb.Reset() + + for _, parentClassName := range class.GetBases() { + var parentClass, ok = parser.State.ClassMap[parentClassName] + if !ok { + continue + } + if parentClass.Module == class.Module { + fmt.Fprintf(bb, "%v\n", parentClassName) + } else { + fmt.Fprintf(bb, "%v.%v\n", goModule(parentClass.Module), parentClassName) + } + } + + return bb.String() + }(), + ) + } + + fmt.Fprintf(bb, "type %v_ITF interface {\n%v%v\n}\n\n", + + class.Name, + + func() string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + for _, parentClassName := range class.GetBases() { + var parentClass, ok = parser.State.ClassMap[parentClassName] + if !ok { + continue + } + if parentClass.Module == class.Module { + fmt.Fprintf(bb, "%v_ITF\n", parentClassName) + } else { + fmt.Fprintf(bb, "%v.%v_ITF\n", goModule(parentClass.Module), parentClassName) + } + } + + return bb.String() + }(), + + fmt.Sprintf("%[1]v_PTR() *%[1]v\n", class.Name), + ) + + fmt.Fprintf(bb, "func (ptr *%[1]v) %[1]v_PTR() *%[1]v {\nreturn ptr\n}\n\n", class.Name) + + if class.Bases == "" { + fmt.Fprintf(bb, "func (ptr *%v) Pointer() unsafe.Pointer {\nif ptr != nil {\nreturn ptr.ptr\n}\nreturn nil\n}\n\n", class.Name) + fmt.Fprintf(bb, "func (ptr *%v) SetPointer(p unsafe.Pointer) {\nif ptr != nil {\nptr.ptr = p\n}\n}\n\n", class.Name) + } else { + fmt.Fprintf(bb, "func (ptr *%v) Pointer() unsafe.Pointer {\nif ptr != nil {\nreturn ptr.%v_PTR().Pointer()\n}\nreturn nil\n}\n\n", class.Name, class.GetBases()[0]) + + fmt.Fprintf(bb, "func (ptr *%v) SetPointer(p unsafe.Pointer) {\nif ptr != nil{\n%v}\n}\n", + + class.Name, + + func() string { + var bb = new(bytes.Buffer) + defer bb.Reset() + + for _, parentClassName := range class.GetBases() { + fmt.Fprintf(bb, "ptr.%v_PTR().SetPointer(p)\n", parentClassName) + } + + return bb.String() + }(), + ) + } + + fmt.Fprintf(bb, ` +func PointerFrom%v(ptr %[2]v_ITF) unsafe.Pointer { + if ptr != nil { + return ptr.%[2]v_PTR().Pointer() + } + return nil +} +`, strings.Title(class.Name), class.Name) + + if class.Module == parser.MOC { + fmt.Fprintf(bb, ` +func New%vFromPointer(ptr unsafe.Pointer) (n *%[2]v) { + if gPtr, ok := qt.Receive(ptr); !ok { + n = new(%[2]v) + n.SetPointer(ptr) + } else { + switch deduced := gPtr.(type) { + case *%[2]v: + n = deduced + + case *%[3]v: + n = &%[2]v{%[4]v: *deduced } + + default: + n = new(%[2]v) + n.SetPointer(ptr) + } + } + return +} +`, strings.Title(class.Name), class.Name, + func() string { + bc := class.GetBases()[0] + if class.Module == parser.State.ClassMap[bc].Module { + return bc + } + return fmt.Sprintf("%v.%v", strings.ToLower(strings.TrimPrefix(parser.State.ClassMap[bc].Module, "Qt")), bc) + }(), class.GetBases()[0]) + } else { + fmt.Fprintf(bb, ` +func New%vFromPointer(ptr unsafe.Pointer) (n *%[2]v) { + n = new(%[2]v) + n.SetPointer(ptr) + return +} +`, strings.Title(class.Name), class.Name) + } + + if !class.HasDestructor() { + if UseStub(stub, module, mode) { + fmt.Fprintf(bb, "\nfunc (ptr *%v) Destroy%v() {}\n\n", class.Name, strings.Title(class.Name)) + } else if !class.IsSubClassOfQObject() { + if UseJs() { + fmt.Fprintf(bb, ` +func (ptr *%[1]v) Destroy%[1]v() { + if ptr != nil { + %v + ptr.SetPointer(nil) + runtime.SetFinalizer(ptr, nil) + } +} + +`, class.Name, func() string { + if class.HasCallbackFunctions() { + return "\nqt.DisconnectAllSignals(ptr.Pointer(), \"\")" + } + return "" + }()) + } else { + fmt.Fprintf(bb, ` +func (ptr *%[1]v) Destroy%[1]v() { + if ptr != nil { + C.free(ptr.Pointer())%v + ptr.SetPointer(nil) + runtime.SetFinalizer(ptr, nil) + } +} + +`, class.Name, func() string { + if class.HasCallbackFunctions() { + return "\nqt.DisconnectAllSignals(ptr.Pointer(), \"\")" + } + return "" + }()) + } + } + } + + if mode == MOC { + if UseJs() { + if parser.UseWasm() { + fmt.Fprintf(bb, "//export callback%[1]v_Constructor\nfunc callback%[1]v_Constructor(_ js.Value, args []js.Value) interface{} {", class.Name) + fmt.Fprint(bb, "\nptr := uintptr(args[0].Int())\n") + } else { + fmt.Fprintf(bb, "//export callback%[1]v_Constructor\nfunc callback%[1]v_Constructor(ptr uintptr) {", class.Name) + } + fmt.Fprintf(bb, "this := New%vFromPointer(unsafe.Pointer(ptr))\nqt.Register(unsafe.Pointer(ptr), this)\n", strings.Title(class.Name)) + } else { + fmt.Fprintf(bb, "//export callback%[1]v_Constructor\nfunc callback%[1]v_Constructor(ptr unsafe.Pointer) {", class.Name) + fmt.Fprintf(bb, "this := New%vFromPointer(ptr)\nqt.Register(ptr, this)\n", strings.Title(class.Name)) + } + + var lastModule string + for _, bcn := range class.GetAllBases() { + if bc := parser.State.ClassMap[bcn]; bc.Module != class.Module { + if len(bc.Constructors) > 0 && lastModule != bc.Module { + if strings.ToLower(bc.Constructors[0])[0] != bc.Constructors[0][0] { + fmt.Fprintf(bb, "this.%v.%v()\n", strings.Title(bc.Name), bc.Constructors[0]) + } + } + lastModule = bc.Module + } + } + + for _, bcn := range append(class.GetAllBases(), class.Name) { + if bc, ok := parser.State.ClassMap[bcn]; ok { + for _, f := range bc.Functions { + if f.Connect == 0 || !f.IsMocFunction { + continue + } + + if class.Name != bcn { + if parser.UseJs() { + fmt.Fprintf(bb, "qt.DisconnectSignal(unsafe.Pointer(ptr), \"%v\")\n", f.Name) + } else { + fmt.Fprintf(bb, "qt.DisconnectSignal(ptr, \"%v\")\n", f.Name) + } + } + } + } + } + + connect := func(class *parser.Class, local bool) { + for _, bcn := range append(class.GetAllBases(), class.Name) { + if bc, ok := parser.State.ClassMap[bcn]; ok { + for _, f := range bc.Functions { + if f.Connect == 0 || !f.IsMocFunction { + continue + } + if (local && f.Target != "") || (!local && f.Target == "") { + continue + } + + name := f.Name + if f.Inbound { + name = strings.Title(name) + } + + if f.Connect == 1 { + if f.Target == "" { + fmt.Fprintf(bb, "this.Connect%v(this.%v)\n", strings.Title(name), name) + } else { + t := f.Target + if strings.Count(t, ".") != 2 { + if !(len(strings.Split(f.Target, ".")) == 2 && strings.Split(f.Target, ".")[0] != "this" && strings.Split(f.Target, ".")[1][:1] == strings.ToLower(strings.Split(f.Target, ".")[1][:1])) { + t = f.Target + "." + name + } + } + tUpper := strings.Split(f.Target, ".") + tUpper[len(tUpper)-1] = strings.Title(tUpper[len(tUpper)-1]) + + if strings.Count(f.Target, ".") >= 2 || (len(strings.Split(f.Target, ".")) == 2 && strings.Split(f.Target, ".")[0] != "this" && strings.Split(f.Target, ".")[1][:1] == strings.ToLower(strings.Split(f.Target, ".")[1][:1])) { + fmt.Fprintf(bb, "this.Connect%v(%v)\n", strings.Title(name), strings.Join(tUpper, ".")) + } else { + fmt.Fprintf(bb, "this.Connect%v(%v.%v)\n", strings.Title(name), f.Target, strings.Title(name)) + } + } + } else { + if f.Target != "" { + t := f.Target + if strings.Count(t, ".") != 2 { + if !(len(strings.Split(f.Target, ".")) == 2 && strings.Split(f.Target, ".")[0] != "this" && strings.Split(f.Target, ".")[1][:1] == strings.ToLower(strings.Split(f.Target, ".")[1][:1])) { + t = f.Target + "." + name + } + } + tCon := strings.Split(f.Target, ".") + tCon[len(tCon)-1] = "Connect" + strings.Title(tCon[len(tCon)-1]) + + if strings.Count(f.Target, ".") >= 2 || (len(strings.Split(f.Target, ".")) == 2 && strings.Split(f.Target, ".")[0] != "this" && strings.Split(f.Target, ".")[1][:1] == strings.ToLower(strings.Split(f.Target, ".")[1][:1])) { + fmt.Fprintf(bb, "%v(this.%v)\n", strings.Join(tCon, "."), name) + } else { + fmt.Fprintf(bb, "%v.Connect%v(this.%v)\n", f.Target, strings.Title(name), name) + } + } + } + } + } + } + + for _, bcn := range append(class.GetAllBases(), class.Name) { + if bc, ok := parser.State.ClassMap[bcn]; ok { + for _, p := range bc.Properties { + if p.Connect == 0 { + continue + } + if (local && p.Target != "") || (!local && p.Target == "") { + continue + } + + name := p.Name + if p.Inbound { + name = strings.Title(name) + } + + if p.Connect == 1 { + if p.Target == "" { + if p.ConnectGet || !(p.ConnectSet || p.ConnectChanged) { + fmt.Fprintf(bb, "this.Connect%v(this.%v)\n", + func() string { + if p.Output == "bool" && !strings.HasPrefix(name, "is") { + return "Is" + strings.Title(name) + } + return strings.Title(name) + }(), + func() string { + if p.Output == "bool" && !strings.HasPrefix(name, "is") { + return "is" + strings.Title(name) + } + return name + }()) + } + if p.ConnectSet || !(p.ConnectGet || p.ConnectChanged) { + fmt.Fprintf(bb, "this.ConnectSet%v(this.set%v)\n", strings.Title(name), strings.Title(name)) + } + if p.ConnectChanged || !(p.ConnectGet || p.ConnectSet) { + fmt.Fprintf(bb, "this.Connect%vChanged(this.%vChanged)\n", strings.Title(name), name) + } + } else { + t := p.Target + if strings.Count(t, ".") < 2 { + if !(len(strings.Split(p.Target, ".")) == 2 && strings.Split(p.Target, ".")[0] != "this" && strings.Split(p.Target, ".")[1][:1] == strings.ToLower(strings.Split(p.Target, ".")[1][:1])) { + t = p.Target + "." + name + } + } + + tSet := strings.Split(t, ".") + tSet[len(tSet)-1] = "ConnectSet" + strings.Title(tSet[len(tSet)-1]) + + tChanged := strings.Split(t, ".") + tChanged[len(tChanged)-1] = strings.Title(tChanged[len(tChanged)-1]) + "Changed" + + tUpper := strings.Split(t, ".") + tUpper[len(tUpper)-1] = strings.Title(tUpper[len(tUpper)-1]) + + tIs := strings.Split(t, ".") + if p.Output == "bool" && !strings.HasPrefix(tIs[len(tIs)-1], "is") { + tIs[len(tIs)-1] = "ConnectIs" + strings.Title(tIs[len(tIs)-1]) + } else { + tIs[len(tIs)-1] = "Connect" + strings.Title(tIs[len(tIs)-1]) + } + + if p.ConnectGet || !(p.ConnectSet || p.ConnectChanged) { + fmt.Fprintf(bb, "%v(this.%v)\n", + strings.Join(tIs, "."), + func() string { + if p.Output == "bool" && !strings.HasPrefix(name, "is") { + return "Is" + strings.Title(name) + } + return strings.Title(name) + }()) + } + if p.ConnectSet || !(p.ConnectGet || p.ConnectChanged) { + fmt.Fprintf(bb, "%v(this.Set%v)\n", strings.Join(tSet, "."), strings.Title(name)) + } + if p.ConnectChanged || !(p.ConnectGet || p.ConnectSet) { + fmt.Fprintf(bb, "this.Connect%vChanged(%v)\n", strings.Title(name), strings.Join(tChanged, ".")) + } + } + } else { + if p.Target != "" { + t := p.Target + if strings.Count(t, ".") < 2 { + if !(len(strings.Split(p.Target, ".")) == 2 && strings.Split(p.Target, ".")[0] != "this" && strings.Split(p.Target, ".")[1][:1] == strings.ToLower(strings.Split(p.Target, ".")[1][:1])) { + t = p.Target + "." + name + } + } + + tSet := strings.Split(t, ".") + tSet[len(tSet)-1] = "Set" + strings.Title(tSet[len(tSet)-1]) + + tChanged := strings.Split(t, ".") + tChanged[len(tChanged)-1] = "Connect" + strings.Title(tChanged[len(tChanged)-1]) + "Changed" + + tUpper := strings.Split(t, ".") + tUpper[len(tUpper)-1] = strings.Title(tUpper[len(tUpper)-1]) + + tIs := strings.Split(t, ".") + if p.Output == "bool" && !strings.HasPrefix(tIs[len(tIs)-1], "is") { + tIs[len(tIs)-1] = "Is" + strings.Title(tIs[len(tIs)-1]) + } else { + tIs[len(tIs)-1] = strings.Title(tIs[len(tIs)-1]) + } + + if p.ConnectGet || !(p.ConnectSet || p.ConnectChanged) { + fmt.Fprintf(bb, "this.Connect%v(%v)\n", + func() string { + if p.Output == "bool" && !strings.HasPrefix(name, "is") { + return "Is" + strings.Title(name) + } + return strings.Title(name) + }(), strings.Join(tIs, ".")) + } + if p.ConnectSet || !(p.ConnectGet || p.ConnectChanged) { + fmt.Fprintf(bb, "this.ConnectSet%v(%v)\n", strings.Title(name), strings.Join(tSet, ".")) + } + if p.ConnectChanged || !(p.ConnectGet || p.ConnectSet) { + fmt.Fprintf(bb, "%v(this.%vChanged)\n", strings.Join(tChanged, "."), strings.Title(name)) + } + } + } + } + } + } + } + + connect(class, true) + + if len(class.Constructors) > 0 { + fmt.Fprintf(bb, "this.%v()\n", class.Constructors[0]) + } + + connect(class, false) + + if UseJs() { + if parser.UseWasm() { + bb.WriteString("\nreturn nil\n") + } + } + + fmt.Fprint(bb, "}\n\n") + } + } + + cTemplate(bb, class, goEnum, goFunction, "\n\n", true) + } + + if UseJs() { + fmt.Fprint(bb, "func init() {\n") + for _, l := range strings.Split(bb.String(), "\n") { + if strings.HasPrefix(l, "//export") { + if parser.UseWasm() { + fmt.Fprintf(bb, "qt.WASM.Set(\"_%[1]v\", js.NewCallback(%[1]v))\n", strings.TrimPrefix(l, "//export ")) + } else { + fmt.Fprintf(bb, "qt.WASM.Set(\"_%[1]v\", %[1]v)\n", strings.TrimPrefix(l, "//export ")) + } + } + } + + if parser.UseWasm() { + //TODO: + } else { + fmt.Fprint(bb, "var module *js.Object\n") + fmt.Fprintf(bb, "if m := js.Global.Get(\"%v\"); m == js.Undefined {\n", goModule(module)) + fmt.Fprint(bb, "\tmodule = new(js.Object)\n") + fmt.Fprintf(bb, "\tjs.Global.Set(\"%v\", module)\n", goModule(module)) + fmt.Fprint(bb, "} else {\n") + fmt.Fprint(bb, "\tmodule = m\n") + fmt.Fprint(bb, "}\n") + } + + for _, c := range parser.SortedClassesForModule(module, true) { + for _, f := range c.Functions { + if f.Meta != parser.CONSTRUCTOR && !f.Static { + continue + } + if strings.Contains(f.Name, "RegisterMetaType") || strings.Contains(f.Name, "RegisterType") { //TODO: + continue + } + if !f.IsSupported() { + continue + } + var ip string + oldsm := f.SignalMode + f.SignalMode = parser.CALLBACK + f.FakeForJSCallback = true + ip = converter.GoHeaderInput(f) + ip = strings.TrimPrefix(ip, "ptr uintptr, ") + f.SignalMode = oldsm + f.FakeForJSCallback = false + var out string + if parser.UseWasm() { + out = "" //TODO: export classes for jsinterop example + } else { + if converter.GoHeaderOutput(f) != "" { + out = fmt.Sprintf("module.Set(\"%v\", func(%v) *js.Object { return qt.MakeWrapper(%v(%v)); })\n", converter.GoHeaderName(f), ip, converter.GoHeaderName(f), converter.GoInputParametersForCallback(f)) + } else { + out = fmt.Sprintf("module.Set(\"%v\", func(%v) { %v(%v); })\n", converter.GoHeaderName(f), ip, converter.GoHeaderName(f), converter.GoInputParametersForCallback(f)) + } + } + if !strings.Contains(out, "unsupported_") && !strings.Contains(out, "C.") && strings.Contains(bb.String(), converter.GoHeaderName(f)+"(") { + bb.WriteString(out) + } + } + + for _, e := range c.Enums { + for _, v := range e.Values { + if v.Name == "ByteOrder" { + continue + } + if parser.UseWasm() { + //TODO: + } else { + fmt.Fprintf(bb, "module.Set(\"%v__%v\", int64(%v__%v))\n", strings.Split(e.Fullname, "::")[0], v.Name, strings.Split(e.Fullname, "::")[0], v.Name) + } + } + } + } + + fmt.Fprint(bb, "}\n") + } + + return preambleGo(module, goModule(module), bb.Bytes(), stub, mode, pkg, target, tags) +} + +func preambleGo(oldModule string, module string, input []byte, stub bool, mode int, pkg, target, tags string) []byte { + var bb = new(bytes.Buffer) + defer bb.Reset() + + if UseStub(stub, oldModule, mode) || UseJs() { + fmt.Fprintf(bb, `%v + +package %v +`, buildTags(oldModule, stub, mode, tags), + + func() string { + if mode == MOC { + return pkg + } + return module + }(), + ) + + } else { + fmt.Fprintf(bb, `%v + +package %v + +//#include +//#include +//#include +//#include "%v.h" +import "C" +`, + + buildTags(oldModule, stub, mode, tags), + + func() string { + if mode == MOC { + return pkg + } + return module + }(), + + func() string { + switch module { + case "androidextras": + { + return fmt.Sprintf("%v_android", module) + } + + case "sailfish": + { + return fmt.Sprintf("%v_sailfish", module) + } + + default: + { + if mode == MINIMAL { + return fmt.Sprintf("%v-minimal", module) + } + + if mode == MOC { + return "moc" + } + + return module + } + } + }(), + ) + } + + inputString := string(input) + if mode == MOC { + for _, lib := range parser.GetLibs() { + mlow := strings.ToLower(lib) + for _, pre := range []string{" ", "\t", "\r", "\n", "!", "*", "(", ")", "[", "]"} { + for _, past := range []string{"NewQ", "PointerFromQ", "Q"} { + inputString = strings.Replace(inputString, fmt.Sprintf("%v%v.%v", pre, mlow, past), fmt.Sprintf("%vstd_%v.%v", pre, mlow, past), -1) + } + } + } + } + + fmt.Fprint(bb, "import (\n") + for _, m := range append(parser.GetLibs(), "qt", "strings", "unsafe", "log", "runtime", "fmt", "errors", "js", "time", "hex", "reflect") { + mlow := strings.ToLower(m) + if strings.Contains(inputString, fmt.Sprintf(" %v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("\t%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("\r%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("\n%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("!%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("*%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("(%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf(")%v.", mlow)) || + strings.Contains(inputString, fmt.Sprintf("std_%v.", mlow)) { + switch mlow { + case "strings", "unsafe", "log", "runtime", "fmt", "errors", "time", "reflect": + fmt.Fprintf(bb, "\"%v\"\n", mlow) + + case "hex": + fmt.Fprintln(bb, "\"encoding/hex\"") + + case "qt": + fmt.Fprintln(bb, "\"github.com/peterq/pan-light/qt\"") + + case "js": + if parser.UseWasm() { + fmt.Fprintln(bb, "\"syscall/js\"") + } else { + fmt.Fprintln(bb, "\"github.com/gopherjs/gopherjs/js\"") + } + + default: + if mode == MOC { + fmt.Fprintf(bb, "std_%[1]v \"github.com/peterq/pan-light/qt/bindings/%[1]v\"\n", mlow) + } else { + fmt.Fprintf(bb, "\"github.com/peterq/pan-light/qt/bindings/%v\"\n", mlow) + } + + if mode == MOC { + parser.LibDeps[parser.MOC] = append(parser.LibDeps[parser.MOC], m) + } + + //TODO: REVIEW + if !UseJs() { + if strings.HasPrefix(target, "ios") && mode == MINIMAL { + oldModuleGo := strings.TrimPrefix(oldModule, "Qt") + + var ( + containsSub bool + containsSelf bool + ) + + for _, l := range parser.LibDeps["build_static"] { + if l == m { + containsSub = true + } + if l == oldModuleGo { + containsSelf = true + } + } + + if !containsSelf || !containsSub { + + if !containsSelf { + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], oldModuleGo) + + switch oldModuleGo { + case "Multimedia": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "MultimediaWidgets") + case "Quick": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "QuickWidgets") + } + } + + if !containsSub { + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], m) + + switch m { + case "Multimedia": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "MultimediaWidgets") + case "Quick": + parser.LibDeps["build_static"] = append(parser.LibDeps["build_static"], "QuickWidgets") + } + } + + } + } + } + //TODO: REVIEW + } + } + } + + if mode == MOC { + for custom, m := range parser.GetCustomLibs(target, tags) { + switch { + case strings.Contains(m, "/vendor/"): + fmt.Fprintf(bb, "\"%v\"\n", custom) + + case strings.Contains(inputString, fmt.Sprintf("%v.", custom)): + fmt.Fprintf(bb, "%v \"%v\"\n", custom, m) + } + } + + for i := range parser.State.MocImports { + fmt.Fprintf(bb, "%v\n", i) + + if strings.HasPrefix(i, ".") { + delete(parser.State.MocImports, i) + } + } + } + + fmt.Fprintln(bb, ")") + + bb.WriteString(inputString) + + out, err := format.Source(renameSubClasses(bb.Bytes())) + if err != nil { + utils.Log.WithError(err).Errorln("failed to format:", pkg, module) + out = bb.Bytes() + } + + //TODO: regexp + if mode == MOC { + pre := string(out) + libsm := make(map[string]struct{}, 0) + for _, c := range parser.State.ClassMap { + if c.Pkg != "" && c.IsSubClassOfQObject() { + libsm[c.Module] = struct{}{} + } + } + + var libs []string + for k := range libsm { + libs = append(libs, k) + } + libs = append(libs, module) + + for _, c := range parser.SortedClassesForModule(strings.Join(libs, ","), true) { + hName := c.Hash() + sep := []string{"\"_", "\n", "(", "_", "callback", "C."} + for _, p := range sep { + for _, s := range sep { + if s == "callback" || s == "C." || (p == "_" && s == "(" && UseJs()) { + continue + } + pre = strings.Replace(pre, p+c.Name+s, p+c.Name+hName+s, -1) + } + } + } + out = []byte(pre) + } + + return out +} + +//TODO: regexp +func renameSubClasses(in []byte) []byte { + for _, c := range parser.State.ClassMap { + if c.Fullname != "" { + sep := []string{"\n", ".", "\"", " ", "*", "(", ")", "{", "C.", "_ITF", "_PTR", " New", ".New", "(New", "\"New", "From", "Destroy"} + for _, p := range sep { + for _, s := range sep { + in = bytes.Replace(in, []byte(p+c.Name+s), []byte(p+strings.Replace(c.Fullname, "::", "_", -1)+s), -1) + } + } + } + } + return in +} diff --git a/qt/tool-chain/binding/templater/template_h.go b/qt/tool-chain/binding/templater/template_h.go new file mode 100644 index 0000000..b80a3ec --- /dev/null +++ b/qt/tool-chain/binding/templater/template_h.go @@ -0,0 +1,105 @@ +package templater + +import ( + "bytes" + "fmt" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func HTemplate(m string, mode int, tags string) []byte { + utils.Log.WithField("module", m).Debug("generating h") + + var bb = new(bytes.Buffer) + defer bb.Reset() + + if m != parser.MOC { + m = "Qt" + m + } + + //header + fmt.Fprintf(bb, "%v\n\n", buildTags(m, false, mode, tags)) + + fmt.Fprint(bb, "#pragma once\n\n") + + var hash string + if m == parser.MOC { + hash = "_" + parser.SortedClassesForModule(m, true)[0].Hash() //TODO: + } + fmt.Fprintf(bb, "#ifndef GO_%v%v_H\n", strings.ToUpper(m), hash) + fmt.Fprintf(bb, "#define GO_%v%v_H\n\n", strings.ToUpper(m), hash) + + fmt.Fprint(bb, "#include \n\n") + + fmt.Fprint(bb, "#ifdef __cplusplus\n") + for _, c := range parser.SortedClassNamesForModule(m, true) { + if parser.State.ClassMap[c].IsSubClassOfQObject() { + if m == parser.MOC { + fmt.Fprintf(bb, "class %v;\n", c) + fmt.Fprintf(bb, "void %[1]v_%[1]v_QRegisterMetaTypes();\n", c) + } else { + fmt.Fprintf(bb, "int %[1]v_%[1]v_QRegisterMetaType();\n", c) + } + } + } + + fmt.Fprint(bb, "extern \"C\" {\n#endif\n\n") + + if !UseJs() { + fmt.Fprintf(bb, "struct %v_PackedString { char* data; long long len; };\n", strings.Title(m)) + fmt.Fprintf(bb, "struct %v_PackedList { void* data; long long len; };\n", strings.Title(m)) + } + + //body + for _, c := range parser.SortedClassesForModule(m, true) { + cTemplate(bb, c, cppEnumHeader, cppFunctionHeader, ";\n", false) + } + + //footer + fmt.Fprint(bb, "\n#ifdef __cplusplus\n}\n#endif\n\n#endif") + + //TODO: regexp + if mode == MOC { + pre := bb.String() + bb.Reset() + libsm := make(map[string]struct{}, 0) + for _, c := range parser.State.ClassMap { + if c.Pkg != "" && c.IsSubClassOfQObject() { + libsm[c.Module] = struct{}{} + } + } + + var libs []string + for k := range libsm { + libs = append(libs, k) + } + libs = append(libs, m) + + for _, c := range parser.SortedClassesForModule(strings.Join(libs, ","), true) { + hName := c.Hash() + sep := []string{"\"_", "LIVE_", " ", "\t", "\n", "\r", "(", ")", ":", ";", "*", "<", ">", "&", "~", "{", "}", "[", "]", "_", "callback"} + for _, p := range sep { + for _, s := range sep { + if s == "callback" { + continue + } + pre = strings.Replace(pre, p+c.Name+s, p+c.Name+hName+s, -1) + } + } + } + bb.WriteString(pre) + } + + if !UseJs() { + return bb.Bytes() + } + tmp := bb.String() + for _, l := range strings.Split(tmp, "\n") { + if strings.Contains(l, "emscripten::val") { + tmp = strings.Replace(tmp, l, "", -1) + } + } + return []byte(tmp) +} diff --git a/qt/tool-chain/binding/templater/templater.go b/qt/tool-chain/binding/templater/templater.go new file mode 100644 index 0000000..2f9784a --- /dev/null +++ b/qt/tool-chain/binding/templater/templater.go @@ -0,0 +1,76 @@ +package templater + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func GenModule(m, target string, mode int) { + if !parser.ShouldBuildForTarget(m, target) { + utils.Log.WithField("module", m).Debug("skip generation") + return + } + utils.Log.WithField("module", m).Debug("generating") + + var suffix string + switch m { + case "AndroidExtras": + suffix = "_android" + + case "Sailfish": + suffix = "_sailfish" + } + + if mode == NONE { + utils.RemoveAll(utils.GoQtPkgPath(strings.ToLower(m))) + utils.MkdirAll(utils.GoQtPkgPath(strings.ToLower(m))) + } + + if mode == MINIMAL { + if suffix != "" { + return + } + + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+"-minimal.cpp"), CppTemplate(m, mode, target, "")) + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+"-minimal.h"), HTemplate(m, mode, "")) + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+"-minimal.go"), GoTemplate(m, false, mode, m, target, "")) + + if !UseStub(false, "Qt"+m, mode) { + CgoTemplate(m, "", target, mode, m, "") + } + + return + } + + if m == "AndroidExtras" { + utils.Save(utils.GoQtPkgPath(strings.ToLower(m), "utils-androidextras_android.go"), utils.Load(filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain"), "get files dir")), "/binding/files/utils-androidextras_android.go"))) + } + + if !UseStub(false, "Qt"+m, mode) { + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+suffix+".cpp"), CppTemplate(m, mode, target, "")) + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+suffix+".h"), HTemplate(m, mode, "")) + } + + //always generate full + if suffix != "" { + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+suffix+".go"), GoTemplate(m, false, mode, m, target, "")) + } + + //may generate stub + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+".go"), GoTemplate(m, suffix != "", mode, m, target, "")) + + if !UseStub(false, "Qt"+m, mode) { + CgoTemplate(m, "", target, mode, m, "") + } + + if utils.QT_DOCKER() { + if idug, ok := os.LookupEnv("IDUG"); ok { + utils.RunCmd(exec.Command("chown", "-R", idug, utils.GoQtPkgPath(strings.ToLower(m))), "chown files to user") + } + } +} diff --git a/qt/tool-chain/ci/darwin.sh b/qt/tool-chain/ci/darwin.sh new file mode 100755 index 0000000..e7590fd --- /dev/null +++ b/qt/tool-chain/ci/darwin.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -ev + +#check env +df -h +diskutil list + +ls $HOME/* +du -sh $HOME/* + +if [ "$QT_HOMEBREW" == "true" ] +then + #download and install qt with brew + brew update + brew install qt5 + brew outdated qt5 || brew upgrade qt5 + ln -s /usr/local/Cellar/qt/5.11.2 $HOME/Desktop/Qt5.11.2 +else + #download and install qt + QT=qt-unified-mac-x64-online + curl -sL --retry 10 --retry-delay 10 -o /tmp/$QT.dmg https://download.qt.io/official_releases/online_installers/$QT.dmg + hdiutil attach -noverify -noautofsck -quiet /tmp/$QT.dmg + QT=qt-unified-mac-x64-3.0.5-online + if [ "$IOS" == "true" ] || [ "$IOS_SIMULATOR" == "true" ] + then + /Volumes/$QT/$QT.app/Contents/MacOS/$QT -v --script $GOPATH/src/github.com/peterq/pan-light/qt/internal/ci/iscript.qs IOS=true + else + /Volumes/$QT/$QT.app/Contents/MacOS/$QT -v --script $GOPATH/src/github.com/peterq/pan-light/qt/internal/ci/iscript.qs DARWIN=true + fi + diskutil unmountDisk disk1 + rm -f /tmp/$QT.dmg + ln -s $HOME/Qt $HOME/Desktop +fi + +if [ "$ANDROID" == "true" ] +then + #download and install android sdk + SDK=sdk-tools-darwin-3859397.zip + curl -sL --retry 10 --retry-delay 10 -o /tmp/$SDK https://dl.google.com/android/repository/$SDK + unzip -qq /tmp/$SDK -d $HOME/android-sdk-macosx/ + rm -f /tmp/$SDK + ln -s $HOME/android-sdk-macosx $HOME/Desktop + + #install deps for android sdk + $HOME/android-sdk-macosx/tools/bin/sdkmanager --list --verbose + echo "y" | $HOME/android-sdk-macosx/tools/bin/sdkmanager "platform-tools" "build-tools;26.0.0" "platforms;android-25" + echo "y" | $HOME/android-sdk-macosx/tools/bin/sdkmanager --update + + #download and install android ndk + NDK=android-ndk-r18b-darwin-x86_64.zip + curl -sL --retry 10 --retry-delay 10 -o /tmp/$NDK https://dl.google.com/android/repository/$NDK + unzip -qq /tmp/$NDK -d $HOME + rm -f /tmp/$NDK + ln -s $HOME/android-ndk-r18b $HOME/Desktop +fi + +#prepare env +sudo chown $USER /usr/local/bin +sudo chown $USER $GOROOT/pkg | true + +#check env +df -h +diskutil list + +ls $HOME/* +du -sh $HOME/* + +exit 0 \ No newline at end of file diff --git a/qt/tool-chain/ci/iscript.qs b/qt/tool-chain/ci/iscript.qs new file mode 100644 index 0000000..fbd376a --- /dev/null +++ b/qt/tool-chain/ci/iscript.qs @@ -0,0 +1,114 @@ +function Controller() +{ + installer.wizardPageInsertionRequested.connect(function(widget, page) + { + installer.removeWizardPage(installer.components()[0], "WorkspaceWidget"); + }) + + installer.autoRejectMessageBoxes(); + installer.installationFinished.connect(function() + { + gui.clickButton(buttons.NextButton); + }) +} + +Controller.prototype.WelcomePageCallback = function() +{ + gui.clickButton(buttons.NextButton, 30000); +} + +Controller.prototype.CredentialsPageCallback = function() +{ + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.IntroductionPageCallback = function() +{ + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.TargetDirectoryPageCallback = function() +{ + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.ComponentSelectionPageCallback = function() +{ + var version = "qt5.5120"; + if (installer.value("VERSION") != "") + { + version = installer.value("VERSION"); + } + + if (installer.value("DARWIN") == "true") + { + gui.currentPageWidget().selectComponent("qt."+version+".clang_64"); + } + + if (installer.value("IOS") == "true") + { + gui.currentPageWidget().selectComponent("qt."+version+".ios"); + } + + if (installer.value("WINDOWS") == "true") + { + gui.currentPageWidget().selectComponent("qt."+version+".win32_mingw49"); + gui.currentPageWidget().selectComponent("qt."+version+".win32_mingw53"); + gui.currentPageWidget().selectComponent("qt."+version+".win64_mingw73"); + } + + if (installer.value("LINUX") == "true") + { + gui.currentPageWidget().selectComponent("qt."+version+".gcc_64"); + } + + gui.currentPageWidget().selectComponent("qt."+version+".qt3d"); + gui.currentPageWidget().selectComponent("qt."+version+".qtcanvas3d"); + gui.currentPageWidget().selectComponent("qt."+version+".qtcharts"); + gui.currentPageWidget().selectComponent("qt."+version+".qtdatavis3d"); + gui.currentPageWidget().selectComponent("qt."+version+".qtlocation"); + gui.currentPageWidget().selectComponent("qt."+version+".qtnetworkauth"); + gui.currentPageWidget().selectComponent("qt."+version+".qtpurchasing"); + gui.currentPageWidget().selectComponent("qt."+version+".qtquickcontrols"); + gui.currentPageWidget().selectComponent("qt."+version+".qtquickcontrols2"); + gui.currentPageWidget().selectComponent("qt."+version+".qtremoteobjects"); + gui.currentPageWidget().selectComponent("qt."+version+".qtscript"); + gui.currentPageWidget().selectComponent("qt."+version+".qtserialbus"); + gui.currentPageWidget().selectComponent("qt."+version+".qtspeech"); + gui.currentPageWidget().selectComponent("qt."+version+".qtvirtualkeyboard"); + gui.currentPageWidget().selectComponent("qt."+version+".qtwebengine"); + gui.currentPageWidget().selectComponent("qt."+version+".qtwebglplugin"); + gui.currentPageWidget().selectComponent("qt."+version+".qtwebview"); + + gui.currentPageWidget().selectComponent("qt."+version+".android_armv7"); + gui.currentPageWidget().selectComponent("qt."+version+".android_x86"); + gui.currentPageWidget().selectComponent("qt."+version+".android_arm64_v8a"); + + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.LicenseAgreementPageCallback = function() +{ + gui.currentPageWidget().AcceptLicenseRadioButton.setChecked(true); + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.StartMenuDirectoryPageCallback = function() +{ + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.ReadyForInstallationPageCallback = function() +{ + gui.clickButton(buttons.NextButton); +} + +Controller.prototype.FinishedPageCallback = function() +{ + var checkBox = gui.currentPageWidget().LaunchQtCreatorCheckBoxForm; + if (checkBox && checkBox.launchQtCreatorCheckBox) + { + checkBox.launchQtCreatorCheckBox.checked = false; + } + gui.clickButton(buttons.FinishButton); +} diff --git a/qt/tool-chain/ci/linux.sh b/qt/tool-chain/ci/linux.sh new file mode 100755 index 0000000..1122eb4 --- /dev/null +++ b/qt/tool-chain/ci/linux.sh @@ -0,0 +1,94 @@ +#!/bin/bash +set -ev + +#check env +df -h + +ls $HOME/* +du -sh $HOME/* + +#needed for headless qt installation +export QT_QPA_PLATFORM=minimal + +#additional deps for multimedia +sudo apt-get -y -qq install libpulse-dev && sudo apt-get -qq clean + +if [ "$QT_PKG_CONFIG" == "true" ] +then + #download and install qt + sudo add-apt-repository -y ppa:forkotov02/opt-qt-5.10.0-trusty + sudo apt-get -qq update + sudo apt-get -y -qq install qt510base qt510connectivity qt510declarative qt510doc qt510location qt510multimedia qt510quickcontrols qt510quickcontrols2 qt510script qt510serialport qt510svg qt510tools qt510translations qt510webchannel qt510webengine qt510websockets qt510x11extras qt510xmlpatterns && sudo apt-get -qq clean +else + #download and install qt + QT=qt-unified-linux-x64-online.run + curl -sL --retry 10 --retry-delay 10 -o /tmp/$QT https://download.qt.io/official_releases/online_installers/$QT + chmod +x /tmp/$QT + /tmp/$QT -v --script $GOPATH/src/github.com/peterq/pan-light/qt/internal/ci/iscript.qs LINUX=true + rm -f /tmp/$QT +fi + +if [ "$QT_MXE" == "true" ] +then + #download and install qt (and wine) for cross compilation + sudo apt-get -y -qq install wine && sudo apt-get -qq clean + echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" | sudo tee --append /etc/apt/sources.list.d/mxeapt.list > /dev/null + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB + sudo apt-get -qq update + + if [ "$QT_MXE_STATIC" == "true" ] + then + if [ "$QT_MXE_ARCH" == "386" ] + then + sudo apt-get -y -qq install mxe-i686-w64-mingw32.static-qt3d mxe-i686-w64-mingw32.static-qtactiveqt mxe-i686-w64-mingw32.static-qtbase mxe-i686-w64-mingw32.static-qtcanvas3d mxe-i686-w64-mingw32.static-qtcharts mxe-i686-w64-mingw32.static-qtconnectivity mxe-i686-w64-mingw32.static-qtdatavis3d mxe-i686-w64-mingw32.static-qtdeclarative mxe-i686-w64-mingw32.static-qtgamepad mxe-i686-w64-mingw32.static-qtgraphicaleffects mxe-i686-w64-mingw32.static-qtimageformats mxe-i686-w64-mingw32.static-qtlocation mxe-i686-w64-mingw32.static-qtmultimedia mxe-i686-w64-mingw32.static-qtofficeopenxml mxe-i686-w64-mingw32.static-qtpurchasing mxe-i686-w64-mingw32.static-qtquickcontrols mxe-i686-w64-mingw32.static-qtquickcontrols2 mxe-i686-w64-mingw32.static-qtscript mxe-i686-w64-mingw32.static-qtscxml mxe-i686-w64-mingw32.static-qtsensors mxe-i686-w64-mingw32.static-qtserialbus mxe-i686-w64-mingw32.static-qtserialport mxe-i686-w64-mingw32.static-qtservice mxe-i686-w64-mingw32.static-qtsvg mxe-i686-w64-mingw32.static-qtsystems mxe-i686-w64-mingw32.static-qttools mxe-i686-w64-mingw32.static-qttranslations mxe-i686-w64-mingw32.static-qtvirtualkeyboard mxe-i686-w64-mingw32.static-qtwebchannel mxe-i686-w64-mingw32.static-qtwebsockets mxe-i686-w64-mingw32.static-qtwinextras mxe-i686-w64-mingw32.static-qtxlsxwriter mxe-i686-w64-mingw32.static-qtxmlpatterns + else + sudo apt-get -y -qq install mxe-x86-64-w64-mingw32.static-qt3d mxe-x86-64-w64-mingw32.static-qtactiveqt mxe-x86-64-w64-mingw32.static-qtbase mxe-x86-64-w64-mingw32.static-qtcanvas3d mxe-x86-64-w64-mingw32.static-qtcharts mxe-x86-64-w64-mingw32.static-qtconnectivity mxe-x86-64-w64-mingw32.static-qtdatavis3d mxe-x86-64-w64-mingw32.static-qtdeclarative mxe-x86-64-w64-mingw32.static-qtgamepad mxe-x86-64-w64-mingw32.static-qtgraphicaleffects mxe-x86-64-w64-mingw32.static-qtimageformats mxe-x86-64-w64-mingw32.static-qtlocation mxe-x86-64-w64-mingw32.static-qtmultimedia mxe-x86-64-w64-mingw32.static-qtofficeopenxml mxe-x86-64-w64-mingw32.static-qtpurchasing mxe-x86-64-w64-mingw32.static-qtquickcontrols mxe-x86-64-w64-mingw32.static-qtquickcontrols2 mxe-x86-64-w64-mingw32.static-qtscript mxe-x86-64-w64-mingw32.static-qtscxml mxe-x86-64-w64-mingw32.static-qtsensors mxe-x86-64-w64-mingw32.static-qtserialbus mxe-x86-64-w64-mingw32.static-qtserialport mxe-x86-64-w64-mingw32.static-qtservice mxe-x86-64-w64-mingw32.static-qtsvg mxe-x86-64-w64-mingw32.static-qtsystems mxe-x86-64-w64-mingw32.static-qttools mxe-x86-64-w64-mingw32.static-qttranslations mxe-x86-64-w64-mingw32.static-qtvirtualkeyboard mxe-x86-64-w64-mingw32.static-qtwebchannel mxe-x86-64-w64-mingw32.static-qtwebsockets mxe-x86-64-w64-mingw32.static-qtwinextras mxe-x86-64-w64-mingw32.static-qtxlsxwriter mxe-x86-64-w64-mingw32.static-qtxmlpatterns + fi + else + if [ "$QT_MXE_ARCH" == "386" ] + then + sudo apt-get -y -qq install mxe-i686-w64-mingw32.shared-qt3d mxe-i686-w64-mingw32.shared-qtactiveqt mxe-i686-w64-mingw32.shared-qtbase mxe-i686-w64-mingw32.shared-qtcanvas3d mxe-i686-w64-mingw32.shared-qtcharts mxe-i686-w64-mingw32.shared-qtconnectivity mxe-i686-w64-mingw32.shared-qtdatavis3d mxe-i686-w64-mingw32.shared-qtdeclarative mxe-i686-w64-mingw32.shared-qtgamepad mxe-i686-w64-mingw32.shared-qtgraphicaleffects mxe-i686-w64-mingw32.shared-qtimageformats mxe-i686-w64-mingw32.shared-qtlocation mxe-i686-w64-mingw32.shared-qtmultimedia mxe-i686-w64-mingw32.shared-qtofficeopenxml mxe-i686-w64-mingw32.shared-qtpurchasing mxe-i686-w64-mingw32.shared-qtquickcontrols mxe-i686-w64-mingw32.shared-qtquickcontrols2 mxe-i686-w64-mingw32.shared-qtscript mxe-i686-w64-mingw32.shared-qtscxml mxe-i686-w64-mingw32.shared-qtsensors mxe-i686-w64-mingw32.shared-qtserialbus mxe-i686-w64-mingw32.shared-qtserialport mxe-i686-w64-mingw32.shared-qtservice mxe-i686-w64-mingw32.shared-qtsvg mxe-i686-w64-mingw32.shared-qtsystems mxe-i686-w64-mingw32.shared-qttools mxe-i686-w64-mingw32.shared-qttranslations mxe-i686-w64-mingw32.shared-qtvirtualkeyboard mxe-i686-w64-mingw32.shared-qtwebchannel mxe-i686-w64-mingw32.shared-qtwebkit mxe-i686-w64-mingw32.shared-qtwebsockets mxe-i686-w64-mingw32.shared-qtwinextras mxe-i686-w64-mingw32.shared-qtxlsxwriter mxe-i686-w64-mingw32.shared-qtxmlpatterns + else + sudo apt-get -y -qq install mxe-x86-64-w64-mingw32.shared-qt3d mxe-x86-64-w64-mingw32.shared-qtactiveqt mxe-x86-64-w64-mingw32.shared-qtbase mxe-x86-64-w64-mingw32.shared-qtcanvas3d mxe-x86-64-w64-mingw32.shared-qtcharts mxe-x86-64-w64-mingw32.shared-qtconnectivity mxe-x86-64-w64-mingw32.shared-qtdatavis3d mxe-x86-64-w64-mingw32.shared-qtdeclarative mxe-x86-64-w64-mingw32.shared-qtgamepad mxe-x86-64-w64-mingw32.shared-qtgraphicaleffects mxe-x86-64-w64-mingw32.shared-qtimageformats mxe-x86-64-w64-mingw32.shared-qtlocation mxe-x86-64-w64-mingw32.shared-qtmultimedia mxe-x86-64-w64-mingw32.shared-qtofficeopenxml mxe-x86-64-w64-mingw32.shared-qtpurchasing mxe-x86-64-w64-mingw32.shared-qtquickcontrols mxe-x86-64-w64-mingw32.shared-qtquickcontrols2 mxe-x86-64-w64-mingw32.shared-qtscript mxe-x86-64-w64-mingw32.shared-qtscxml mxe-x86-64-w64-mingw32.shared-qtsensors mxe-x86-64-w64-mingw32.shared-qtserialbus mxe-x86-64-w64-mingw32.shared-qtserialport mxe-x86-64-w64-mingw32.shared-qtservice mxe-x86-64-w64-mingw32.shared-qtsvg mxe-x86-64-w64-mingw32.shared-qtsystems mxe-x86-64-w64-mingw32.shared-qttools mxe-x86-64-w64-mingw32.shared-qttranslations mxe-x86-64-w64-mingw32.shared-qtvirtualkeyboard mxe-x86-64-w64-mingw32.shared-qtwebchannel mxe-x86-64-w64-mingw32.shared-qtwebkit mxe-x86-64-w64-mingw32.shared-qtwebsockets mxe-x86-64-w64-mingw32.shared-qtwinextras mxe-x86-64-w64-mingw32.shared-qtxlsxwriter mxe-x86-64-w64-mingw32.shared-qtxmlpatterns + fi + fi + + sudo apt-get -qq clean +fi + +if [ "$ANDROID" == "true" ]; then + #install openjdk8 + echo "export JDK_DIR=/usr/lib/jvm/java-8-openjdk-amd64" >> $HOME/.profile + sudo add-apt-repository -y ppa:openjdk-r/ppa + sudo apt-get -qq update + sudo apt-get -y -qq install openjdk-8-jdk && sudo apt-get -qq clean + + #download and install android sdk + SDK=sdk-tools-linux-3859397.zip + curl -sL --retry 10 --retry-delay 10 -o /tmp/$SDK https://dl.google.com/android/repository/$SDK + unzip -qq /tmp/$SDK -d $HOME/android-sdk-linux/ + rm -f /tmp/$SDK + + #install deps for android sdk + $HOME/android-sdk-linux/tools/bin/sdkmanager --list --verbose + echo "y" | $HOME/android-sdk-linux/tools/bin/sdkmanager "platform-tools" "build-tools;26.0.0" "platforms;android-25" + $HOME/android-sdk-linux/tools/bin/sdkmanager --update + + #download and install android ndk + NDK=android-ndk-r18b-linux-x86_64.zip + curl -sL --retry 10 --retry-delay 10 -o /tmp/$NDK https://dl.google.com/android/repository/$NDK + unzip -qq /tmp/$NDK -d $HOME + rm -f /tmp/$NDK +fi + +#prepare env +sudo chown $USER /usr/local/bin +sudo chown $USER $GOROOT/pkg | true + +#check env +df -h + +ls $HOME/* +du -sh $HOME/* + +exit 0 diff --git a/qt/tool-chain/cmd/cmd.go b/qt/tool-chain/cmd/cmd.go new file mode 100644 index 0000000..c52362b --- /dev/null +++ b/qt/tool-chain/cmd/cmd.go @@ -0,0 +1,781 @@ +package cmd + +import ( + "flag" + "fmt" + "os" + "os/exec" + "os/user" + "path/filepath" + "runtime" + "strconv" + "strings" + + "github.com/sirupsen/logrus" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +var buildVersion = "no build version" + +func ParseFlags() bool { + if runtime.GOOS == "windows" { + utils.Log.SetFormatter(&logrus.TextFormatter{DisableColors: true}) + } + var ( + debug = flag.Bool("debug", false, "print debug logs") + help = flag.Bool("help", false, "print help") + p = flag.Int("p", runtime.NumCPU(), "specify the number of cpu's to be used") + qt_api = flag.String("qt_api", "", "specify the api version to be used") + qt_dir = flag.String("qt_dir", utils.QT_DIR(), "export QT_DIR") + qt_version = flag.String("qt_version", utils.QT_VERSION(), "export QT_VERSION") + version = flag.Bool("version", false, "print build version (if available)") + ) + flag.Parse() + + if *debug { + utils.Log.Level = logrus.DebugLevel + } + + runtime.GOMAXPROCS(*p) + + if api := *qt_api; api != "" { + os.Setenv("QT_API", api) + } + + if dir := *qt_dir; dir != utils.QT_DIR() { + os.Setenv("QT_DIR", dir) + } + + if version := *qt_version; version != utils.QT_VERSION() { + os.Setenv("QT_VERSION", version) + } + + if *version { + println(buildVersion) + os.Exit(0) + } + + return help != nil && *help +} + +func InitEnv(target string) { + if target != runtime.GOOS || runtime.GOARCH != "amd64" { + return + } + + switch runtime.GOOS { + case "linux": + if utils.QT_PKG_CONFIG() { + return + } + case "darwin": + if utils.QT_HOMEBREW() || utils.QT_MACPORTS() || utils.QT_NIX() { + return + } + case "windows": + if utils.QT_MSYS2() { + return + } + + defer func() { + qtenvPath := filepath.Join(filepath.Dir(utils.ToolPath("qmake", target)), "qtenv2.bat") + for _, s := range strings.Split(utils.Load(qtenvPath), "\r\n") { + if strings.HasPrefix(s, "set PATH") { + os.Setenv("PATH", strings.TrimPrefix(strings.Replace(s, "%PATH%", os.Getenv("PATH"), -1), "set PATH=")) + break + } + } + + for i, dPath := range []string{filepath.Join(runtime.GOROOT(), "bin", "qtenv.bat"), filepath.Join(utils.GOBIN(), "qtenv.bat")} { + sPath := qtenvPath + existed := utils.ExistsFile(dPath) + if existed { + utils.RemoveAll(dPath) + } + err := os.Link(sPath, dPath) + if i != 0 { + continue + } + if err == nil { + if !existed { + utils.Log.Infof("successfully created %v symlink in your PATH (%v)", filepath.Base(dPath), dPath) + } + } else { + utils.Log.Warnf("failed to create %v symlink in your PATH (%v); please use %v instead", filepath.Base(dPath), dPath, sPath) + } + } + }() + default: + return + } + + if !utils.ExistsDir(utils.QT_DIR()) { + qt_dir := strings.TrimSpace(utils.RunCmd(utils.GoList("{{.Dir}}", "github.com/therecipe/env_"+runtime.GOOS+"_amd64_"+strconv.Itoa(utils.QT_VERSION_NUM())[:3]), "get env dir")) + + switch runtime.GOOS { + case "linux", "darwin", "windows": + os.Setenv("QT_DIR", qt_dir) + if api := utils.QT_API(""); api == "" { + os.Setenv("QT_API", utils.QT_VERSION()) + } + } + + var err error + var link string + switch runtime.GOOS { + case "linux": + link = filepath.Join("/var", "tmp", ".env_linux_amd64") + utils.RemoveAll(link) + err = os.Symlink(qt_dir, link) + case "darwin": + link = filepath.Join("/Applications", ".env_darwin_amd64") + utils.RemoveAll(link) + err = os.Symlink(qt_dir, link) + case "windows": + link = filepath.Join("C:\\", "Users", "Public", "env_windows_amd64") + utils.RemoveAll(link) + _, err = utils.RunCmdOptionalError(exec.Command("cmd", "/C", "mklink", "/J", link, qt_dir), "create symlink for env") + + if err == nil { + link = filepath.Join("C:\\", "Users", "Public", "env_windows_amd64_Tools") + utils.RemoveAll(link) + _, err = utils.RunCmdOptionalError(exec.Command("cmd", "/C", "mklink", "/J", link, strings.TrimSpace(utils.RunCmd(utils.GoList("{{.Dir}}", "github.com/therecipe/env_"+runtime.GOOS+"_amd64_"+strconv.Itoa(utils.QT_VERSION_NUM())[:3]+"/Tools"), "get env dir"))), "create symlink for env") + } + } + if err != nil { + if !(utils.ExistsFile(link) || utils.ExistsDir(link)) { + utils.Log.WithError(err).Warn("failed to create env symlink; fallback to patching binaries instead (this won't work for go modules)\r\nplease open an issue") + cmd := exec.Command("go", "run", "patch.go", qt_dir) + cmd.Dir = qt_dir + _, err = utils.RunCmdOptionalError(cmd, "patch env binaries") + if err != nil { + utils.Log.Warn("failed to patch binaries (do you use go modules?)\r\nyou won't be able to simply \"go build/run\" your application, but qtdeploy-ing applications should work nevertheless\r\nplease open an issue") + } + } + } + + var webenginearchive string + var bindir string + switch runtime.GOOS { + case "linux": + webenginearchive = filepath.Join(qt_dir, utils.QT_VERSION(), "gcc_64", "lib", "libQt5WebEngineCore.so."+utils.QT_VERSION()+".gz") + bindir = filepath.Join(qt_dir, utils.QT_VERSION(), "gcc_64", "bin") + case "darwin": + webenginearchive = filepath.Join(qt_dir, utils.QT_VERSION(), "clang_64", "lib", "QtWebEngineCore.framework", "Versions", "Current", "QtWebEngineCore.gz") + bindir = filepath.Join(qt_dir, utils.QT_VERSION(), "clang_64", "bin") + case "windows": + return + } + + if utils.ExistsFile(webenginearchive) { + _, err := utils.RunCmdOptionalError(exec.Command("gzip", "-d", webenginearchive), "uncompress webengine") + if err != nil { + utils.Log.WithError(err).Warn("failed to uncompress webengine, (do you use go modules?)") + } else { + utils.RemoveAll(webenginearchive) + } + } + + if len(bindir) != 0 && utils.UseGOMOD("") && strings.Contains(qt_dir, "@") { + filepath.Walk(bindir, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() { + return err + } + if filepath.Ext(info.Name()) != ".conf" { + utils.RunCmd(exec.Command("chmod", "+x", path), "restore exec permission") + } + return nil + }) + } + } +} + +func Docker(arg []string, target, path string, writeCacheToHost bool) { + virtual(arg, target, path, writeCacheToHost, true, "") +} + +func Vagrant(arg []string, target, path string, writeCacheToHost bool, system string) { + virtual(arg, target, path, writeCacheToHost, false, system) +} + +func virtual(arg []string, target, path string, writeCacheToHost bool, docker bool, system string) { + arg = append(arg, target) + if system == "" { + system = "linux" + } + + var image string + switch target { + case "windows": + if utils.QT_MXE_ARCH() == "amd64" || utils.QT_MSYS2_ARCH() == "amd64" { + if utils.QT_MXE_STATIC() || utils.QT_MSYS2_STATIC() { + image = "windows_64_static" + } else { + image = "windows_64_shared" + } + } else { + if utils.QT_MXE_STATIC() || utils.QT_MSYS2_STATIC() { + image = "windows_32_static" + } else { + image = "windows_32_shared" + } + } + if !docker { + image = target + } + + case "linux", "rpi1", "rpi2", "rpi3", "js", "wasm": + image = target + + case "android", "android-emulator": + image = "android" + + case "sailfish", "sailfish-emulator": + image = "sailfish" + + default: + switch system { + case "darwin": + case "linux": + if (strings.HasPrefix(target, "windows") || strings.HasPrefix(target, "ubports")) && strings.Contains(target, "_") { + image = target + } else if docker && strings.Contains(target, "_") { + utils.Log.Fatalf("%v is currently not supported", target) + } + case "windows": + } + } + if image != "" { + target = image + } + + var args []string + if docker { + args = []string{"run", "--rm"} + } else { + if target == "pkg_config" { + args = []string{"source /home/vagrant/.profile"} + } + } + if runtime.GOOS == "linux" { + u, err := user.Current() + if err != nil { + utils.Log.WithError(err).Error("failed to lookup current user") + } else { + args = append(args, "-e", fmt.Sprintf("IDUG=%v:%v", u.Uid, u.Gid)) + } + } + + paths := make([]string, 0) + + for i, gp := range strings.Split(utils.GOPATH(), string(filepath.ListSeparator)) { + if runtime.GOOS == "windows" { + gp = "//" + strings.ToLower(gp[:1]) + gp[1:] + } + gp = strings.Replace(gp, "\\", "/", -1) + gp = strings.Replace(gp, ":", "", -1) + + var pathprefix string + if docker { + args = append(args, []string{"-v", fmt.Sprintf("%v:/media/sf_GOPATH%v", gp, i)}...) + } else if system == "windows" { + pathprefix = "C:" + } + paths = append(paths, fmt.Sprintf(pathprefix+"/media/sf_GOPATH%v", i)) + } + + var gpfs string + if docker { + gpfs = "/home/user/work" + } else { + switch system { + case "linux", "docker": + gpfs = "/home/vagrant/gopath" + case "darwin": + gpfs = "/Users/vagrant/gopath" + case "windows": + gpfs = "C:/gopath" + } + } + + pathseperator := ":" + if system == "windows" { + pathseperator = ";" + } + gpath := strings.Join(paths, pathseperator) + if writeCacheToHost { + gpath += pathseperator + gpfs + args = append(args, []string{"-e", "QT_STUB=true"}...) + } else { + gpath = gpfs + pathseperator + gpath + } + + if utils.QT_DEBUG() { + args = append(args, []string{"-e", "QT_DEBUG=true"}...) + } + + if utils.QT_DEBUG_QML() { + args = append(args, []string{"-e", "QT_DEBUG_QML=true"}...) + } + + if utils.QT_DEBUG_CONSOLE() { + args = append(args, []string{"-e", "QT_DEBUG_CONSOLE=true"}...) + } + + if api := utils.QT_API(""); api != "" { + args = append(args, []string{"-e", "QT_API=" + api}...) + } + + if utils.CI() { + args = append(args, []string{"-e", "CI=true"}...) + } + + if utils.QT_WEBKIT() { + args = append(args, []string{"-e", "QT_WEBKIT=true"}...) + } + + //TODO: flag for shared GOCACHE + + if docker { + args = append(args, []string{"-e", "GOPATH=" + gpath}...) + } else { + args = append(args, []string{"-e", "QT_VAGRANT=true"}...) + args = append(args, []string{"-e", "GOPATH='" + gpath + "'"}...) + } + + if docker { + args = append(args, []string{"-i", fmt.Sprintf("therecipe/qt:%v", image)}...) + } else { + for i, a := range args { + if a == "-e" { + args[i] = "&&" + args[i+1] = "export " + args[i+1] + } + } + if args[0] == "&&" { + args = args[1:] + } + args = append(args, "&&") + + if system == "windows" { + if strings.Contains(target, "_") { + args = append(args, []string{"C:/msys64/usr/bin/bash -l -c"}...) + arg[0] = "'C:/gopath/bin/" + arg[0] + } else { + args = append(args, []string{"C:/windows/system32/cmd /C"}...) + } + } + } + + args = append(args, arg...) + + var found bool + for i, gp := range strings.Split(utils.GOPATH(), string(filepath.ListSeparator)) { + gp = filepath.Clean(gp) + if strings.HasPrefix(path, gp) { + if !docker && system == "windows" && strings.Contains(target, "_") { + args = append(args, strings.Replace(strings.Replace(path, gp, fmt.Sprintf("C:/media/sf_GOPATH%v", i), -1), "\\", "/", -1)) + } else { + args = append(args, strings.Replace(strings.Replace(path, gp, fmt.Sprintf("/media/sf_GOPATH%v", i), -1), "\\", "/", -1)) + } + found = true + break + } + } + + if !found && path != "" { + utils.Log.Panicln("Project needs to be inside GOPATH", path, utils.GOPATH()) + } + + if docker { + for i := range args { + if strings.HasPrefix(args[i], "windows_") { + args[i] = "windows" + } + if strings.HasPrefix(args[i], "ubports_") { + args[i] = "ubports" + } + } + + utils.RunCmd(exec.Command("docker", args...), fmt.Sprintf("deploy binary for %v on %v with docker", target, runtime.GOOS)) + } else { + switch target { + case "ios-simulator": + target = "ios" + case "android-emulator": + target = "android" + } + + upCmd := exec.Command("vagrant", "up", "--no-provision", target) + upCmd.Dir = utils.GoQtPkgPath("tool-chain", "vagrant", system) + utils.RunCmd(upCmd, fmt.Sprintf("vagrant up for %v/%v on %v", system, target, runtime.GOOS)) + + command := strings.Join(args, " ") + command = strings.Replace(command, " pkg_config ", " linux ", -1) + command = strings.Replace(command, " homebrew ", " desktop ", -1) + for _, v := range []string{"windows_32_shared", "windows_32_static", "windows_64_shared", "windows_64_static"} { + command = strings.Replace(command, " "+v+" ", " windows ", -1) + } + if system == "windows" && strings.Contains(target, "_") { + command += "'" + } + + var cmd *exec.Cmd + if system == "windows" { + cmd = exec.Command("vagrant", []string{"ssh", target, "--", command}...) + } else { + cmd = exec.Command("vagrant", []string{"ssh", "-c", command, target}...) + } + + cmd.Dir = utils.GoQtPkgPath("tool-chain", "vagrant", system) + utils.RunCmd(cmd, fmt.Sprintf("deploy binary for %v/%v on %v with vagrant", system, target, runtime.GOOS)) + + haltCmd := exec.Command("vagrant", "halt", target) + haltCmd.Dir = utils.GoQtPkgPath("tool-chain", "vagrant", system) + utils.RunCmd(haltCmd, fmt.Sprintf("vagrant halt for %v/%v on %v", system, target, runtime.GOOS)) + } +} + +func BuildEnv(target, name, depPath string) (map[string]string, []string, []string, string) { + + var tags []string + var ldFlags []string + var out string + var env map[string]string + + switch target { + case "android": + tags = []string{target} + ldFlags = []string{"-s", "-w"} + out = filepath.Join(depPath, "libgo_base.so") + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "android", + "GOARCH": "arm", + "GOARM": "7", + + "CGO_ENABLED": "1", + "CC": filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "llvm", "prebuilt", runtime.GOOS+"-x86_64", "bin", "clang"), + "CXX": filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "llvm", "prebuilt", runtime.GOOS+"-x86_64", "bin", "clang++"), + + "CGO_CPPFLAGS": fmt.Sprintf("-D__ANDROID_API__=16 -target armv7-none-linux-androideabi -gcc-toolchain %v --sysroot=%v -isystem %v", + filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "arm-linux-androideabi-4.9", "prebuilt", runtime.GOOS+"-x86_64"), + filepath.Join(utils.ANDROID_NDK_DIR(), "sysroot"), + filepath.Join(utils.ANDROID_NDK_DIR(), "sysroot", "usr", "include", "arm-linux-androideabi")), + "CGO_LDFLAGS": fmt.Sprintf("-D__ANDROID_API__=16 -target armv7-none-linux-androideabi -gcc-toolchain %v --sysroot=%v", + filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "arm-linux-androideabi-4.9", "prebuilt", runtime.GOOS+"-x86_64"), + filepath.Join(utils.ANDROID_NDK_DIR(), "platforms", "android-16", "arch-arm")), + } + if runtime.GOOS == "windows" { + env["TMP"] = os.Getenv("TMP") + env["TEMP"] = os.Getenv("TEMP") + } + + case "android-emulator": + tags = []string{strings.Replace(target, "-", "_", -1)} + ldFlags = []string{"-s", "-w"} + out = filepath.Join(depPath, "libgo_base.so") + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "android", + "GOARCH": "386", + + "CGO_ENABLED": "1", + "CC": filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "llvm", "prebuilt", runtime.GOOS+"-x86_64", "bin", "clang"), + "CXX": filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "llvm", "prebuilt", runtime.GOOS+"-x86_64", "bin", "clang++"), + + "CGO_CPPFLAGS": fmt.Sprintf("-D__ANDROID_API__=16 -target i686-none-linux-android -mstackrealign -gcc-toolchain %v --sysroot=%v -isystem %v", + filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "x86-4.9", "prebuilt", runtime.GOOS+"-x86_64"), + filepath.Join(utils.ANDROID_NDK_DIR(), "sysroot"), + filepath.Join(utils.ANDROID_NDK_DIR(), "sysroot", "usr", "include", "i686-linux-android")), + "CGO_LDFLAGS": fmt.Sprintf("-D__ANDROID_API__=16 -target i686-none-linux-android -mstackrealign -gcc-toolchain %v --sysroot=%v", + filepath.Join(utils.ANDROID_NDK_DIR(), "toolchains", "x86-4.9", "prebuilt", runtime.GOOS+"-x86_64"), + filepath.Join(utils.ANDROID_NDK_DIR(), "platforms", "android-16", "arch-x86")), + } + if runtime.GOOS == "windows" { + env["TMP"] = os.Getenv("TMP") + env["TEMP"] = os.Getenv("TEMP") + } + + case "ios", "ios-simulator": + tags = []string{"ios"} + ldFlags = []string{"-w"} + out = filepath.Join(depPath, "libgo.a") + + GOARCH := "amd64" + CLANGDIR := "iPhoneSimulator" + CLANGPLAT := utils.IPHONESIMULATOR_SDK_DIR() + CLANGFLAG := "ios-simulator" + CLANGARCH := "x86_64" + if target == "ios" { + GOARCH = "arm64" + CLANGDIR = "iPhoneOS" + CLANGPLAT = utils.IPHONEOS_SDK_DIR() + CLANGFLAG = "iphoneos" + CLANGARCH = "arm64" + } + + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": runtime.GOOS, + "GOARCH": GOARCH, + + "CGO_ENABLED": "1", + + //TODO: move flags into template_cgo_qmake ? + "CGO_CPPFLAGS": fmt.Sprintf("-isysroot %v/Contents/Developer/Platforms/%v.platform/Developer/SDKs/%v -m%v-version-min=10.0 -arch %v", utils.XCODE_DIR(), CLANGDIR, CLANGPLAT, CLANGFLAG, CLANGARCH), + "CGO_LDFLAGS": fmt.Sprintf("-isysroot %v/Contents/Developer/Platforms/%v.platform/Developer/SDKs/%v -m%v-version-min=10.0 -arch %v", utils.XCODE_DIR(), CLANGDIR, CLANGPLAT, CLANGFLAG, CLANGARCH), + } + + case "darwin": + ldFlags = []string{"-w"} + out = filepath.Join(depPath, fmt.Sprintf("%v.app/Contents/MacOS/%v", name, name)) + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "darwin", + "GOARCH": "amd64", + + "CGO_ENABLED": "1", + } + + case "windows": + ldFlags = []string{"-s", "-w"} + if !utils.QT_DEBUG_CONSOLE() { + ldFlags = append(ldFlags, "-H=windowsgui") + } + if runtime.GOOS != target { + tags = []string{"windows"} + } + out = filepath.Join(depPath, name) + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "TMP": os.Getenv("TMP"), + "TEMP": os.Getenv("TEMP"), + + "GOOS": "windows", + "GOARCH": "386", + + "CGO_ENABLED": "1", + } + + if utils.QT_VERSION_NUM() >= 5120 { + env["GOARCH"] = "amd64" + if strings.Contains(utils.QT_DIR(), "env_windows_amd64") { + env["CGO_LDFLAGS"] = filepath.Join("C:\\", "Users", "Public", "env_windows_amd64", "Tools", "mingw730_64", "x86_64-w64-mingw32", "lib", "libmsvcrt.a") + } + } + + if runtime.GOOS == target { + if utils.QT_MSYS2() { + env["GOARCH"] = utils.QT_MSYS2_ARCH() + // use gcc shipped with msys2 + env["PATH"] = filepath.Join(utils.QT_MSYS2_DIR(), "bin") + ";" + env["PATH"] + } else { + // use gcc shipped with qt installation + path := filepath.Join(utils.QT_DIR(), "Tools", "mingw730_64", "bin") + if !utils.ExistsDir(path) { + path = strings.Replace(path, "mingw730_64", "mingw530_32", -1) + } + if !utils.ExistsDir(path) { + path = strings.Replace(path, "mingw530_32", "mingw492_32", -1) + } + env["PATH"] = path + ";" + env["PATH"] + } + } else { + delete(env, "TMP") + delete(env, "TEMP") + + env["GOARCH"] = utils.QT_MXE_ARCH() + env["CC"] = utils.QT_MXE_BIN("gcc") + env["CXX"] = utils.QT_MXE_BIN("g++") + } + + case "linux", "ubports": + ldFlags = []string{"-s", "-w"} + out = filepath.Join(depPath, name) + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "linux", + "GOARCH": utils.GOARCH(), + + "CGO_ENABLED": "1", + } + + if utils.QT_VERSION_NUM() <= 5051 { + env["CGO_CXXFLAGS"] = "-std=c++11" + } + + if arm, ok := os.LookupEnv("GOARM"); ok { + env["GOARM"] = arm + env["CC"] = os.Getenv("CC") + env["CXX"] = os.Getenv("CXX") + } + + if utils.QT_UBPORTS() || target == "ubports" { + tags = []string{"ubports", utils.QT_UBPORTS_VERSION()} + + if utils.QT_UBPORTS_ARCH() == "arm" { + env["GOARCH"] = "arm" + + if env["GOARM"] == "" { + env["GOARM"] = "7" + } + + if env["CC"] == "" || env["CXX"] == "" { + env["CC"] = "arm-linux-gnueabihf-gcc" + env["CXX"] = "arm-linux-gnueabihf-g++" + } + } + } + + if target == "linux" { + env["CGO_LDFLAGS"] = "-Wl,-rpath,$ORIGIN/lib -Wl,--disable-new-dtags" + } + + case "rpi1", "rpi2", "rpi3": + tags = []string{target} + ldFlags = []string{"-s", "-w"} + out = filepath.Join(depPath, name) + + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "linux", + "GOARCH": "arm", + "GOARM": "7", + + "CGO_ENABLED": "1", + "CC": fmt.Sprintf("%v/arm-bcm2708/%v/bin/arm-linux-gnueabihf-gcc", utils.RPI_TOOLS_DIR(), utils.RPI_COMPILER()), + "CXX": fmt.Sprintf("%v/arm-bcm2708/%v/bin/arm-linux-gnueabihf-g++", utils.RPI_TOOLS_DIR(), utils.RPI_COMPILER()), + } + + if target == "rpi1" { + env["GOARM"] = "6" + } + + case "sailfish": + tags = []string{target} + ldFlags = []string{"-s", "-w"} + out = filepath.Join(depPath, "harbour-"+name) + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "linux", + "GOARCH": "arm", + "GOARM": "7", + + "CGO_ENABLED": "1", + "CC": "/srv/mer/toolings/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "/opt/cross/bin/armv7hl-meego-linux-gnueabi-gcc", + "CXX": "/srv/mer/toolings/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "/opt/cross/bin/armv7hl-meego-linux-gnueabi-g++", + + //TODO: use plain -I and -L instead ? + "CPATH": "/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-armv7hl/usr/include", + "LIBRARY_PATH": "/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-armv7hl/usr/lib:/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-armv7hl/lib:/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-armv7hl/usr/lib/pulseaudio", + + //TODO: move flags into template_cgo_qmake ? + "CGO_LDFLAGS": "--sysroot=/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-armv7hl/", + } + + case "sailfish-emulator": + tags = []string{strings.Replace(target, "-", "_", -1)} + ldFlags = []string{"-s", "-w"} + out = filepath.Join(depPath, "harbour-"+name) + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "linux", + "GOARCH": "386", + + "CGO_ENABLED": "1", + "CC": "/srv/mer/toolings/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "/opt/cross/bin/i486-meego-linux-gnu-gcc", + "CXX": "/srv/mer/toolings/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "/opt/cross/bin/i486-meego-linux-gnu-g++", + + //TODO: use plain -I and -L instead ? + "CPATH": "/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-i486/usr/include", + "LIBRARY_PATH": "/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-i486/usr/lib:/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-i486/lib:/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-i486/usr/lib/pulseaudio", + + //TODO: move flags into template_cgo_qmake ? + "CGO_LDFLAGS": "--sysroot=/srv/mer/targets/SailfishOS-" + utils.QT_SAILFISH_VERSION() + "-i486/", + } + + case "js", "wasm": + tags = []string{target} + out = filepath.Join(depPath, "go") + env = map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": runtime.GOOS, + "GOARCH": runtime.GOARCH, + + "CGO_ENABLED": "0", + } + + if target == "wasm" { + env["GOOS"] = "js" + env["GOARCH"] = "wasm" + } + + env["EM_CONFIG"] = filepath.Join(os.Getenv("HOME"), ".emscripten") + for _, l := range strings.Split(utils.Load(env["EM_CONFIG"]), "\n") { + l = strings.Replace(l, "'", "", -1) + switch { + case strings.HasPrefix(l, "LLVM_ROOT="): + env["LLVM_ROOT"] = strings.Split(l, "=")[1] + case strings.HasPrefix(l, "BINARYEN_ROOT="): + env["BINARYEN_ROOT"] = strings.Split(l, "=")[1] + case strings.HasPrefix(l, "NODE_JS="): + env["NODE_JS"] = strings.TrimSuffix(strings.Split(l, "=")[1], "/node") + case strings.HasPrefix(l, "EMSCRIPTEN_ROOT="): + env["EMSCRIPTEN"] = strings.Split(l, "=")[1] + env["EMSDK"] = strings.Split(env["EMSCRIPTEN"], "/emscripten/1.")[0] + } + } + env["PATH"] = env["PATH"] + ":" + env["EMSDK"] + ":" + env["LLVM_ROOT"] + ":" + env["NODE_JS"] + ":" + env["EMSCRIPTEN"] + } + + if runtime.GOOS != target || strings.Contains(runtime.Version(), "1.10") { + env["CGO_CFLAGS_ALLOW"] = utils.CGO_CFLAGS_ALLOW() + env["CGO_CXXFLAGS_ALLOW"] = utils.CGO_CXXFLAGS_ALLOW() + env["CGO_LDFLAGS_ALLOW"] = utils.CGO_LDFLAGS_ALLOW() + } + + if flags := utils.GOFLAGS(); len(flags) != 0 { + env["GOFLAGS"] = flags + } + + for _, e := range os.Environ() { + es := strings.Split(e, "=") + if _, ok := env[es[0]]; !ok { + env[es[0]] = strings.Join(es[1:], "=") + } + } + + return env, tags, ldFlags, out +} diff --git a/qt/tool-chain/cmd/deploy/assets.go b/qt/tool-chain/cmd/deploy/assets.go new file mode 100644 index 0000000..ddf25c3 --- /dev/null +++ b/qt/tool-chain/cmd/deploy/assets.go @@ -0,0 +1,785 @@ +package deploy + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "golang.org/x/crypto/ssh" + + "github.com/peterq/pan-light/qt/tool-chain/cmd/rcc" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +//linux + +func linux_sh(target, name string) string { + bb := new(bytes.Buffer) + defer bb.Reset() + + fmt.Fprint(bb, "#!/bin/bash\n") + fmt.Fprint(bb, "appname=`basename $0 | sed s,\\.sh$,,`\n\n") + fmt.Fprint(bb, "dirname=`dirname $0`\n") + fmt.Fprint(bb, "tmp=\"${dirname#?}\"\n\n") + fmt.Fprint(bb, "if [ \"${dirname%$tmp}\" != \"/\" ]; then\n") + fmt.Fprint(bb, "dirname=$PWD/$dirname\n") + fmt.Fprint(bb, "fi\n") + + if strings.HasPrefix(target, "rpi") { + fmt.Fprint(bb, "export DISPLAY=\":0\"\n") + fmt.Fprint(bb, "export LD_PRELOAD=\"/opt/vc/lib/libGLESv2.so /opt/vc/lib/libEGL.so\"\n") + } + + if utils.QT_PKG_CONFIG() { + libDir := strings.TrimSpace(utils.RunCmd(exec.Command("pkg-config", "--variable=libdir", "Qt5Core"), fmt.Sprintf("get lib dir for %v on %v", target, runtime.GOOS))) + miscDir := utils.QT_MISC_DIR() + + fmt.Fprintf(bb, "export LD_LIBRARY_PATH=\"%v\"\n", libDir) + fmt.Fprintf(bb, "export QT_PLUGIN_PATH=\"%v\"\n", filepath.Join(miscDir, "plugins")) + fmt.Fprintf(bb, "export QML_IMPORT_PATH=\"%v\"\n", filepath.Join(miscDir, "qml")) + fmt.Fprintf(bb, "export QML2_IMPORT_PATH=\"%v\"\n", filepath.Join(miscDir, "qml")) + } else { + libDir := "lib" + if name == libDir { + libDir = "libs" + } + fmt.Fprintf(bb, "export LD_LIBRARY_PATH=\"$dirname/%v\"\n", libDir) + fmt.Fprint(bb, "export QT_PLUGIN_PATH=\"$dirname/plugins\"\n") + fmt.Fprint(bb, "export QML_IMPORT_PATH=\"$dirname/qml\"\n") + fmt.Fprint(bb, "export QML2_IMPORT_PATH=\"$dirname/qml\"\n") + } + fmt.Fprint(bb, "$dirname/$appname \"$@\"\n") + + return bb.String() +} + +//android + +func android_config(target, path, depPath string) string { + jsonStruct := &struct { + Qt string `json:"qt"` + Sdk string `json:"sdk"` + SdkBuildToolsRevision string `json:"sdkBuildToolsRevision"` + Ndk string `json:"ndk"` + Toolchainprefix string `json:"toolchain-prefix"` + Toolprefix string `json:"tool-prefix"` + Toolchainversion string `json:"toolchain-version"` + Ndkhost string `json:"ndk-host"` + Targetarchitecture string `json:"target-architecture"` + AndroidExtraLibs string `json:"android-extra-libs"` + AndroidPackageSourceDirectory string `json:"android-package-source-directory"` + Qmlrootpath string `json:"qml-root-path"` + StdcppPath string `json:"stdcpp-path"` + Applicationbinary string `json:"application-binary"` + }{ + Qt: filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "android_armv7"), + Sdk: utils.ANDROID_SDK_DIR(), + SdkBuildToolsRevision: "28.0.3", + Ndk: utils.ANDROID_NDK_DIR(), + Toolchainprefix: "arm-linux-androideabi", + Toolprefix: "arm-linux-androideabi", + Toolchainversion: "4.9", + Ndkhost: runtime.GOOS + "-x86_64", + Targetarchitecture: "armeabi-v7a", + AndroidExtraLibs: filepath.Join(depPath, "libgo_base.so"), + AndroidPackageSourceDirectory: filepath.Join(path, target), + Qmlrootpath: path, + StdcppPath: filepath.Join(utils.ANDROID_NDK_DIR(), "sources", "cxx-stl", "llvm-libc++", "libs", "armeabi-v7a", "libc++_shared.so"), + Applicationbinary: filepath.Join(depPath, "libgo.so"), + } + + if target == "android-emulator" { + jsonStruct.Qt = filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "android_x86") + jsonStruct.Toolchainprefix = "x86" + jsonStruct.Toolprefix = "i686-linux-android" + jsonStruct.Targetarchitecture = "x86" + jsonStruct.StdcppPath = filepath.Join(utils.ANDROID_NDK_DIR(), "sources", "cxx-stl", "llvm-libc++", "libs", jsonStruct.Targetarchitecture, "libc++_shared.so") + } + + if utils.QT_DOCKER() { + switch target { + case "android": + jsonStruct.AndroidExtraLibs += "," + filepath.Join(os.Getenv("HOME"), "openssl-1.0.2q-arm", "libcrypto.so") + "," + filepath.Join(os.Getenv("HOME"), "openssl-1.0.2q-arm", "libssl.so") + case "android-emulator": + jsonStruct.AndroidExtraLibs += "," + filepath.Join(os.Getenv("HOME"), "openssl-1.0.2q-x86", "libcrypto.so") + "," + filepath.Join(os.Getenv("HOME"), "openssl-1.0.2q-x86", "libssl.so") + } + } + + out, err := json.Marshal(jsonStruct) + if err != nil { + utils.Log.WithError(err).Panicf("failed to create json-config file for androiddeployqt on %v", runtime.GOOS) + } + return strings.Replace(string(out), `\\`, `/`, -1) +} + +//darwin + +func darwin_plist(name string) string { + return fmt.Sprintf(` + + + + CFBundleExecutable + %[1]v + CFBundleGetInfoString + Created by Qt/QMake + CFBundleIconFile + %[1]v.icns + CFBundleIdentifier + com.yourcompany.%[1]v + CFBundleName + %[1]v + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + LSMinimumSystemVersion + 10.11 + NOTE + This file was generated by Qt/QMake. + NSPrincipalClass + NSApplication + NSHighResolutionCapable + + NSSupportsAutomaticGraphicsSwitching + + + +`, name) +} + +func darwin_pkginfo() string { + return "APPL????\n" +} + +func darwin_nix_script(name string) string { + return fmt.Sprintf(`#!/bin/bash +export PATH=$HOME/.nix-profile/bin:$PATH +cd "${0%%/*}" +./%v_bin +`, name) +} + +//ios + +func ios_c_main_wrapper() string { + bb := new(bytes.Buffer) + bb.WriteString("#include \"libgo.h\"\n") + for _, n := range rcc.ResourceNames { + fmt.Fprintf(bb, "extern int qInitResources_%v();\n", n) + } + bb.WriteString("int main(int argc, char *argv[]) {\n") + for _, n := range rcc.ResourceNames { + fmt.Fprintf(bb, "qInitResources_%v();\n", n) + } + bb.WriteString("go_main_wrapper(argc, argv);\n}") + return bb.String() +} + +func ios_plist(name string) string { + return fmt.Sprintf(` + + + + CFBundleDisplayName + %[1]v + CFBundleExecutable + main + CFBundleGetInfoString + Created by Qt/QMake + CFBundleIdentifier + %[2]v + CFBundleName + %[1]v + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0.0 + LSRequiresIPhoneOS + + MinimumOSVersion + ${IPHONEOS_DEPLOYMENT_TARGET} + NOTE + This file was generated by Qt/QMake. + UILaunchStoryboardName + LaunchScreen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + QtRunLoopIntegrationDisableSeparateStack + + + +`, name, strings.Replace(name, "_", "", -1)) +} + +func ios_launchscreen(name string) string { + return fmt.Sprintf(` + + + + + + + + + + + + + + + + + + + + + + + + + + + `, name) +} + +func ios_appicon() string { + return `{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} +` +} + +func ios_xcodeproject() string { + return `// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 254BB84E1B1FD08900C56DE9 /* Images.xcassets */; }; + 254BB8681B1FD16500C56DE9 /* main in Resources */ = {isa = PBXBuildFile; fileRef = 254BB8671B1FD16500C56DE9 /* main */; }; + 25916F411CE65FF600695115 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 25916F401CE65FF600695115 /* LaunchScreen.xib */; }; + 25F26AED1CE6675E0045FFBA /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 25F26AEC1CE6675E0045FFBA /* Default-568h@2x.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 254BB83E1B1FD08900C56DE9 /* main.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = main.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 254BB8421B1FD08900C56DE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 254BB84E1B1FD08900C56DE9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 254BB8671B1FD16500C56DE9 /* main */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = main; sourceTree = ""; }; + 25916F401CE65FF600695115 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; + 25F26AEC1CE6675E0045FFBA /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 254BB8351B1FD08900C56DE9 = { + isa = PBXGroup; + children = ( + 254BB8671B1FD16500C56DE9 /* main */, + 254BB8421B1FD08900C56DE9 /* Info.plist */, + 254BB84E1B1FD08900C56DE9 /* Images.xcassets */, + 25916F401CE65FF600695115 /* LaunchScreen.xib */, + 25F26AEC1CE6675E0045FFBA /* Default-568h@2x.png */, + 254BB83F1B1FD08900C56DE9 /* products */, + ); + sourceTree = ""; + usesTabs = 0; + }; + 254BB83F1B1FD08900C56DE9 /* products */ = { + isa = PBXGroup; + children = ( + 254BB83E1B1FD08900C56DE9 /* main.app */, + ); + name = products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 254BB83D1B1FD08900C56DE9 /* main */ = { + isa = PBXNativeTarget; + buildConfigurationList = 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */; + buildPhases = ( + 254BB83C1B1FD08900C56DE9 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = main; + productName = main; + productReference = 254BB83E1B1FD08900C56DE9 /* main.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 254BB8361B1FD08900C56DE9 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0630; + ORGANIZATIONNAME = Developer; + TargetAttributes = { + 254BB83D1B1FD08900C56DE9 = { + CreatedOnToolsVersion = 6.3.1; + }; + }; + }; + buildConfigurationList = 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "project" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 254BB8351B1FD08900C56DE9; + productRefGroup = 254BB83F1B1FD08900C56DE9 /* products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 254BB83D1B1FD08900C56DE9 /* main */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 254BB83C1B1FD08900C56DE9 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 254BB8681B1FD16500C56DE9 /* main in Resources */, + 25F26AED1CE6675E0045FFBA /* Default-568h@2x.png in Resources */, + 25916F411CE65FF600695115 /* LaunchScreen.xib in Resources */, + 254BB84F1B1FD08900C56DE9 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 254BB8601B1FD08900C56DE9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 254BB8631B1FD08900C56DE9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 254BB8391B1FD08900C56DE9 /* Build configuration list for PBXProject "project" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 254BB8601B1FD08900C56DE9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 254BB8611B1FD08900C56DE9 /* Build configuration list for PBXNativeTarget "main" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 254BB8631B1FD08900C56DE9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 254BB8361B1FD08900C56DE9 /* Project object */; +} +` +} + +//sailfish + +func sailfish_spec(name string) string { + return fmt.Sprintf(`# +# Do NOT Edit the Auto-generated Part! +# Generated by: spectacle version 0.27 +# + +Name: harbour-%[1]v + +# >> macros +# << macros + +Summary: Put your summary here +Version: 0.1 +Release: 1 +Group: Qt/Qt +License: MIT +Source0: %%{name}-%%{version}.tar.bz2 + +%%description +Put your description here + + +%%prep +%%setup -q -n %%{name}-%%{version} + +# >> setup +# << setup + +%%build +# >> build pre +# << build pre + +# >> build post +# << build post + +%%install +rm -rf %%{buildroot} +# >> install pre +# << install pre +install -d %%{buildroot}%%{_bindir} +install -p -m 0755 %%(pwd)/%%{name} %%{buildroot}%%{_bindir}/%%{name} +install -d %%{buildroot}%%{_datadir}/applications +install -d %%{buildroot}%%{_datadir}/%%{name} +install -d %%{buildroot}%%{_datadir}/icons/hicolor/86x86/apps +install -m 0444 -t %%{buildroot}%%{_datadir}/icons/hicolor/86x86/apps %%{name}.png +install -p %%(pwd)/%[1]v.desktop %%{buildroot}%%{_datadir}/applications/%%{name}.desktop + +# >> install post +# << install post + +desktop-file-install --delete-original \ + --dir %%{buildroot}%%{_datadir}/applications \ + %%{buildroot}%%{_datadir}/applications/%%{name}.desktop + +%%files +%%defattr(-,root,root,-) +%%{_bindir} +%%{_datadir}/%%{name} +%%{_datadir}/icons/hicolor/86x86/apps +%%{_datadir}/applications/%%{name}.desktop + +# >> files +# << files`, name) +} + +func sailfish_desktop(name string) string { + return fmt.Sprintf(`[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Type=Application +X-Nemo-Application-Type=generic +Comment=Put your comment here +Name=%[1]v +Icon=harbour-%[1]v +Exec=harbour-%[1]v`, name) +} + +func sailfish_ssh(port, login string, cmd ...string) error { + + typ := "SailfishOS_Emulator" + if port == "2222" { + typ = "engine" + } + + signer, err := ssh.ParsePrivateKey([]byte(utils.Load(filepath.Join(utils.SAILFISH_DIR(), "vmshare", "ssh", "private_keys", typ, login)))) + if err != nil { + return err + } + + client, err := ssh.Dial("tcp", "localhost:"+port, &ssh.ClientConfig{User: login, Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, HostKeyCallback: ssh.InsecureIgnoreHostKey()}) + if err != nil { + return err + } + defer client.Close() + + sess, err := client.NewSession() + if err != nil { + return err + } + + output, err := sess.CombinedOutput(strings.Join(cmd, " ")) + if err != nil { + utils.Log.WithField("cmd", strings.Join(cmd, " ")).Debugf("failed to run ssh cmd for %v on %v", typ, runtime.GOOS) + return errors.New(string(output)) + } + + return nil +} + +func ubports_desktop(name string) string { + return fmt.Sprintf(`[Desktop Entry] +Name=%[1]v +Exec=%[1]v +Icon=logo.svg +Terminal=false +Type=Application +X-Ubuntu-Touch=true`, name) +} + +func ubports_apparmor() string { + return `{ + "policy_groups": [], + "policy_version": 1.3 +}` +} + +func ubports_manifest(name string) string { + return fmt.Sprintf(`{ + "name": "%[1]v", + "description": "description", + "architecture": "%[2]v", + "title": "%[1]v", + "hooks": { + "%[1]v": { + "apparmor": "%[1]v.apparmor", + "desktop": "%[1]v.desktop" + } + }, + "version": "1.0", + "maintainer": "maintainer_name ", + "framework" : "ubuntu-sdk-15.04.6" +}`, name, func() string { + if utils.QT_UBPORTS_ARCH() == "arm" { + return "armhf" + } + return "amd64" + }()) +} + +func relink(env map[string]string, target string) string { + return fmt.Sprintf(`#!/bin/bash +set -ev + +#GO_VERSION: %v +#GO_HOST_OS: %v +#GO_HOST_ARCH: %v +#QT_VERSION: %v + +export GOOS=%v +export GOARCH=%v +export GOARM=%v +export CC=%v +export CXX=%v + +go tool link -f -o $PWD/relinked -importcfg $PWD/b001/importcfg.link -buildmode=%v -w -extld=%v $PWD/b001/_pkg_.a`, + + runtime.Version(), + runtime.GOOS, + runtime.GOARCH, + utils.QT_VERSION(), + + env["GOOS"], + env["GOARCH"], + func() string { + if env["GOARCH"] == "arm" { + return env["GOARM"] + } + return "" + + }(), + env["CC"], + env["CXX"], + + func() string { + switch target { + case "ios", "ios-simulator": + return "c-archive" + case "android", "android-emulator": + return "c-shared" + default: + return "exe" + } + }(), + + func() string { + switch target { + case "ios", "ios-simulator", "darwin": + return "clang++" + default: + return "g++" + } + }()) +} + +//js/wasm + +func js_c_main_wrapper(target string) string { + bb := new(bytes.Buffer) + bb.WriteString("#include \n") + for _, n := range rcc.ResourceNames { + fmt.Fprintf(bb, "extern int qInitResources_%v();\n", n) + } + bb.WriteString("int main(int argc, char *argv[]) {\n") + for _, n := range rcc.ResourceNames { + fmt.Fprintf(bb, "qInitResources_%v();\n", n) + } + + //TODO: use emscripten_sync_run_in_main_runtime_thread once thread support is there ? + bb.WriteString("emscripten_run_script(\"Module._goMain()\");\n") + + bb.WriteString("return 0;\n") + bb.WriteString("}") + return bb.String() +} + +func wasm_js() string { + return ` + + if (!WebAssembly.instantiateStreaming) { // polyfill + WebAssembly.instantiateStreaming = async (resp, importObject) => { + const source = await (await resp).arrayBuffer(); + return await WebAssembly.instantiate(source, importObject); + }; + } + + let go = new Go(); + let instance; + + let fetchPromise = fetch("go.wasm"); + WebAssembly.instantiateStreaming(fetchPromise, go.importObject).then((result) => { + instance = result.instance; + }).catch((err) => { + //console.log(err); + + //fallback for wrong MIME type + fetchPromise.then((response) => + response.arrayBuffer() + ).then((bytes) => + WebAssembly.instantiate(bytes, go.importObject) + ).then((result) => + instance = result.instance + ); + }); + + Module._goMain = function() { + go.run(instance); + }; +})();` +} diff --git a/qt/tool-chain/cmd/deploy/build.go b/qt/tool-chain/cmd/deploy/build.go new file mode 100644 index 0000000..4eaa050 --- /dev/null +++ b/qt/tool-chain/cmd/deploy/build.go @@ -0,0 +1,211 @@ +package deploy + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "time" + + "github.com/sirupsen/logrus" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func build(mode, target, path, ldFlagsCustom, tagsCustom, name, depPath string, fast, comply bool) { + env, tags, ldFlags, out := cmd.BuildEnv(target, name, depPath) + if ((!fast || utils.QT_STUB()) && !utils.QT_FAT()) || target == "js" || target == "wasm" { + tags = append(tags, "minimal") + } + if tagsCustom != "" { + tags = append(tags, strings.Split(tagsCustom, " ")...) + } + if utils.QT_DEBUG_QML() && target == runtime.GOOS { + out = filepath.Join(depPath, name) + } + + var ending string + switch target { + case "android", "android-emulator", "ios", "ios-simulator": + utils.Save(filepath.Join(path, "cgo_main_wrapper.go"), "package main\nimport (\n\"C\"\n\"os\"\n\"unsafe\"\n)\n//export go_main_wrapper\nfunc go_main_wrapper(argc C.int, argv unsafe.Pointer) {\nos.Args=make([]string,int(argc))\nfor i,b := range (*[1<<3]*C.char)(argv)[:int(argc):int(argc)] {\nos.Args[i] = C.GoString(b)\n}\nmain()\n}") + case "windows": + ending = ".exe" + case "sailfish", "sailfish-emulator": + if !utils.QT_SAILFISH() { + build_sailfish(target, path, ldFlagsCustom, name) + return + } + case "js": + build_js(target, path, env, tags, out) + return + case "wasm": + ending = ".wasm" + case "linux": + if fast || utils.QT_PKG_CONFIG() { + delete(env, "CGO_LDFLAGS") + } + } + + var pattern string + if strings.Contains(runtime.Version(), "1.1") || strings.Contains(runtime.Version(), "devel") { + pattern = "all=" + } + + if utils.Log.Level == logrus.DebugLevel && target != "wasm" { + ldFlags = append(ldFlags, "-extldflags=-v") + } + + cmd := exec.Command("go", "build", "-p", strconv.Itoa(runtime.GOMAXPROCS(0)), "-v") + if len(ldFlags) > 0 { + cmd.Args = append(cmd.Args, fmt.Sprintf("-ldflags=%v%v", pattern, escapeFlags(ldFlags, ldFlagsCustom))) + } + cmd.Args = append(cmd.Args, "-o", out+ending) + + cmd.Dir = path + + if fast && !utils.QT_STUB() { + cmd.Args = append(cmd.Args, "-i") + } + + if comply { + utils.MkdirAll(depPath + "_obj") + cmd.Env = append(cmd.Env, fmt.Sprintf("GOTMPDIR=%v", depPath+"_obj")) + cmd.Args = append(cmd.Args, "-a", "-x", "-work") + } else if utils.Log.Level == logrus.DebugLevel { + cmd.Args = append(cmd.Args, "-x") + } + + cmd.Args = append(cmd.Args, fmt.Sprintf("-tags=\"%v\"", strings.Join(tags, "\" \""))) + + if target != runtime.GOOS { + cmd.Args = append(cmd.Args, []string{"-pkgdir", filepath.Join(utils.MustGoPath(), "pkg", fmt.Sprintf("%v_%v_%v", strings.Replace(target, "-", "_", -1), env["GOOS"], env["GOARCH"]))}...) + } + + switch target { + case "android", "android-emulator": + cmd.Args = append(cmd.Args, "-buildmode", "c-shared") + case "ios", "ios-simulator": + cmd.Args = append(cmd.Args, "-buildmode", "c-archive") + } + + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + + utils.RunCmd(cmd, fmt.Sprintf("build for %v on %v", target, runtime.GOOS)) + + if target == "darwin" && !fast { + strip := exec.Command("strip", "-x", out) //TODO: -u -r + strip.Dir = path + utils.RunCmd(strip, fmt.Sprintf("strip binary for %v on %v", target, runtime.GOOS)) + } + + utils.RemoveAll(filepath.Join(path, "cgo_main_wrapper.go")) + + if comply { + dirs, err := ioutil.ReadDir(depPath + "_obj") + if err != nil { + utils.Log.WithError(err).Error("failed to read object dir") + } + + var randname string + for _, dir := range dirs { + if strings.HasPrefix(dir.Name(), "go-build") { + randname = dir.Name() + os.Rename(filepath.Join(depPath+"_obj", dir.Name()), depPath+"_objreal") + utils.RemoveAll(depPath + "_obj") + os.Rename(depPath+"_objreal", depPath+"_obj") + break + } + } + + walkFn := func(fpath string, info os.FileInfo, err error) error { + if err != nil { + return err + } + switch info.Name() { + case "_pkg_.a": + case "importcfg.link": + pre := utils.Load(fpath) + pre = strings.Replace(pre, filepath.Join(depPath+"_obj", randname), ".", -1) + utils.Save(fpath, pre) + default: + if !info.IsDir() || info.Name() == "exe" { + utils.RemoveAll(fpath) + } + } + return nil + } + filepath.Walk(depPath+"_obj", walkFn) + + utils.SaveExec(filepath.Join(depPath+"_obj", "relink.sh"), relink(env, target)) + } +} + +func build_sailfish(target, path, ldFlagsCustom, name string) { + //TODO: ldFlagsCustom, tags + + if !strings.Contains(path, utils.MustGoPath()) { + utils.Log.Panicln("Project needs to be inside GOPATH; have:", path, "want:", utils.MustGoPath()) + } + + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "registervm", filepath.Join(utils.SAILFISH_DIR(), "mersdk", "Sailfish OS Build Engine", "Sailfish OS Build Engine.vbox")), fmt.Sprintf("register mersdk for %v on %v", target, runtime.GOOS)) + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "sharedfolder", "add", "Sailfish OS Build Engine", "--name", "GOROOT", "--hostpath", runtime.GOROOT(), "--automount"), fmt.Sprintf("share GOROOT dir for %v on %v", target, runtime.GOOS)) + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "sharedfolder", "add", "Sailfish OS Build Engine", "--name", "GOPATH", "--hostpath", utils.MustGoPath(), "--automount"), fmt.Sprintf("share GOPATH dir for %v on %v", target, runtime.GOOS)) + + if runtime.GOOS == "windows" { + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "startvm", "--type", "headless", "Sailfish OS Build Engine"), fmt.Sprintf("start vbox mersdk for %v on %v", target, runtime.GOOS)) + } else { + utils.RunCmdOptional(exec.Command("nohup", filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "startvm", "--type", "headless", "Sailfish OS Build Engine"), fmt.Sprintf("start vbox mersdk for %v on %v", target, runtime.GOOS)) + } + + time.Sleep(10 * time.Second) + + for _, l := range []string{"libmpc.so.3", "libmpfr.so.4", "libgmp.so.10", "libpthread_nonshared.a", "libc_nonshared.a"} { + sailfish_ssh("2222", "root", "ln", "-s", fmt.Sprintf("/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/usr/lib/%v", l), fmt.Sprintf("/usr/lib/%v", l)) + } + + arch, gcc := "i486", "gnu" + if target == "sailfish" { + arch, gcc = "armv7hl", "gnueabi" + } + + sailfish_ssh("2222", "root", "ln", "-s", fmt.Sprintf("/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/bin/%v-meego-linux-%v-as", arch, gcc), fmt.Sprintf("/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/libexec/gcc/%v-meego-linux-%v/4.8.3/as", arch, gcc)) + sailfish_ssh("2222", "root", "ln", "-s", fmt.Sprintf("/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/bin/%v-meego-linux-%v-ld", arch, gcc), fmt.Sprintf("/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/libexec/gcc/%v-meego-linux-%v/4.8.3/ld", arch, gcc)) + + var pattern string + if strings.Contains(runtime.Version(), "1.1") || strings.Contains(runtime.Version(), "devel") { + pattern = "all=" + } + + //TODO: + var err error + if target == "sailfish-emulator" { + err = sailfish_ssh("2222", "root", "cd", strings.Replace(strings.Replace(path, utils.MustGoPath(), "/media/sf_GOPATH/", -1), "\\", "/", -1), "&&", "GOROOT=/media/sf_GOROOT", "GOPATH=/media/sf_GOPATH", "PATH=$PATH:$GOROOT/bin/linux_386", "GOOS=linux", "GOARCH=386", "CGO_ENABLED=1", "CGO_CFLAGS_ALLOW=.*", "CGO_CXXFLAGS_ALLOW=.*", "CGO_LDFLAGS_ALLOW=.*", "CC=/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/bin/i486-meego-linux-gnu-gcc", "CXX=/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/bin/i486-meego-linux-gnu-g++", "CPATH=/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/include", "LIBRARY_PATH=/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/lib:/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/lib:/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/lib/pulseaudio", "CGO_LDFLAGS=--sysroot=/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/", "go", "build", fmt.Sprintf("-ldflags=%v\"-s -w\"", pattern), "-tags=\"minimal sailfish_emulator\"", "-o", "deploy/"+target+"/harbour-"+name) + } else { + err = sailfish_ssh("2222", "root", "cd", strings.Replace(strings.Replace(path, utils.MustGoPath(), "/media/sf_GOPATH/", -1), "\\", "/", -1), "&&", "GOROOT=/media/sf_GOROOT", "GOPATH=/media/sf_GOPATH", "PATH=$PATH:$GOROOT/bin/linux_386", "GOOS=linux", "GOARCH=arm", "GOARM=7", "CGO_ENABLED=1", "CGO_CFLAGS_ALLOW=.*", "CGO_CXXFLAGS_ALLOW=.*", "CGO_LDFLAGS_ALLOW=.*", "CC=/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/bin/armv7hl-meego-linux-gnueabi-gcc", "CXX=/srv/mer/toolings/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"/opt/cross/bin/armv7hl-meego-linux-gnueabi-g++", "CPATH=/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-armv7hl/usr/include", "LIBRARY_PATH=/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-armv7hl/usr/lib:/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-armv7hl/lib:/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-armv7hl/usr/lib/pulseaudio", "CGO_LDFLAGS=--sysroot=/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-armv7hl/", "go", "build", "-x", "-v", fmt.Sprintf("-ldflags=%v\"-s -w\"", pattern), "-tags=\"minimal sailfish\"", "-o", "deploy/"+target+"/harbour-"+name) + } + if err != nil { + println(err.Error()) + utils.Log.Panicf("failed to build for %v on %v", target, runtime.GOOS) + } +} + +func build_js(target string, path string, env map[string]string, tags []string, out string) { + cmd := exec.Command(filepath.Join(utils.GOBIN(), "gopherjs"), "build", ".", "-v", "-m", "-o", filepath.Join(filepath.Dir(out), "go.js")) + cmd.Dir = path + + //TODO (bug in gopherjs?): cmd.Args = append(cmd.Args, fmt.Sprintf("--tags=\"%v\"", strings.Join(tags[1:], " "))) + cmd.Args = append(cmd.Args, fmt.Sprintf("--tags=%v", tags[1])) + + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + + utils.RunCmd(cmd, fmt.Sprintf("build for %v on %v", target, runtime.GOOS)) +} diff --git a/qt/tool-chain/cmd/deploy/build_escape.go b/qt/tool-chain/cmd/deploy/build_escape.go new file mode 100644 index 0000000..39aee32 --- /dev/null +++ b/qt/tool-chain/cmd/deploy/build_escape.go @@ -0,0 +1,38 @@ +package deploy + +import ( + "fmt" + "strings" +) + +func escapeFlags(ldFlags []string, ldFlagsCustom string) string { + for _, s := range []string{"\"", "'"} { + var newldFlagsCustom []string + var insideQuotes bool + for i, f := range strings.Split(ldFlagsCustom, s) { + if i > 0 { + if !insideQuotes { + if strings.Contains(f, " ") { + insideQuotes = true + f = strings.Replace(f, " ", "_DONT_ESCAPE_", -1) + } + } else { + insideQuotes = false + } + } + newldFlagsCustom = append(newldFlagsCustom, f) + } + if len(newldFlagsCustom) > 0 { + ldFlagsCustom = strings.Join(newldFlagsCustom, "") + } + } + + if len(ldFlagsCustom) > 0 { + ldFlags = append(ldFlags, strings.Split(ldFlagsCustom, " ")...) + } + + if out := strings.Replace(strings.Join(ldFlags, "\" \""), "_DONT_ESCAPE_", " ", -1); len(out) > 0 { + return fmt.Sprintf("\"%v\"", out) + } + return "" +} diff --git a/qt/tool-chain/cmd/deploy/build_test.go b/qt/tool-chain/cmd/deploy/build_test.go new file mode 100644 index 0000000..017765c --- /dev/null +++ b/qt/tool-chain/cmd/deploy/build_test.go @@ -0,0 +1,74 @@ +package deploy + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func Test_escapeFlags(t *testing.T) { + + tmpfile := filepath.Join(os.TempDir(), "escapeFlags.go") + defer os.Remove(tmpfile) + if err := ioutil.WriteFile(tmpfile, []byte("package main;var foo, abc string;func main() { println(foo, abc) }"), 0644); err != nil { + t.Fatal(err) + } + + var pattern string + if strings.Contains(runtime.Version(), "1.1") || strings.Contains(runtime.Version(), "devel") { + pattern = "all=" + } + + for _, flags := range [][]string{ + {}, + {"-w"}, + {"-w", "-s"}, + {"-w", "-s", "-extldflags=-v"}, + {"-w", "-s", "-extldflags=-v"}, + } { + for i, tc := range []string{ + "", + "-X main.foo=bar", + "-X \"main.foo=bar\"", + "-X 'main.foo=bar'", + } { + cmd := exec.Command("go", "run", "-v", fmt.Sprintf("-ldflags=%v%v", pattern, escapeFlags(flags, tc)), tmpfile) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err, string(out), cmd.Args) + } + outC := "bar" + if tc == "" { + outC = "" + } + if outT := strings.TrimSpace(string(out)); outT != outC { + t.Fatal(i, outT, "!=", outC) + } + } + + for i, tc := range []string{ + "", + "-X \"main.foo=bar baz\" -X \"main.abc=bbb ddd\"", + "-X \"main.foo=bar baz\" -X 'main.abc=bbb ddd'", + "-X 'main.foo=bar baz' -X 'main.abc=bbb ddd'", + } { + cmd := exec.Command("go", "run", "-v", fmt.Sprintf("-ldflags=%v%v", pattern, escapeFlags(flags, tc)), tmpfile) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatal(err, string(out), cmd.Args) + } + outC := "bar baz bbb ddd" + if tc == "" { + outC = "" + } + if outT := strings.TrimSpace(string(out)); outT != outC { + t.Fatal(i, outT, "!=", outC) + } + } + } +} diff --git a/qt/tool-chain/cmd/deploy/bundle.go b/qt/tool-chain/cmd/deploy/bundle.go new file mode 100644 index 0000000..3547b5e --- /dev/null +++ b/qt/tool-chain/cmd/deploy/bundle.go @@ -0,0 +1,743 @@ +package deploy + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/binding/templater" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/moc" + "github.com/peterq/pan-light/qt/tool-chain/cmd/rcc" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func bundle(mode, target, path, name, depPath string, tagsCustom string, fast bool) { + copy := func(src, dst string) { + copy := "cp" + + if runtime.GOOS == "windows" { + copy = "xcopy" + + //TODO: --> + src = strings.TrimSuffix(src, "/") + src = strings.TrimSuffix(src, "\\") + + dst = strings.TrimSuffix(dst, "/") + dst = strings.TrimSuffix(dst, "\\") + //<-- + } + + var args []string + if _, err := ioutil.ReadDir(src); err == nil { + if runtime.GOOS != "windows" { + args = append(args, "-R") + } + } + + var suffix string + if _, err := ioutil.ReadDir(dst); err != nil { + if runtime.GOOS == "windows" { + suffix = "*" + } + } + + utils.RunCmd(exec.Command(copy, append(args, src, dst+suffix)...), fmt.Sprintf("copy %v to %v for %v on %v", filepath.Base(src), filepath.Base(dst), target, runtime.GOOS)) + } + + switch target { + case "darwin": + + //copy default assets + utils.Save(filepath.Join(depPath, name+".app", "Contents", "Info.plist"), darwin_plist(name)) + utils.Save(filepath.Join(depPath, name+".app", "Contents", "PkgInfo"), darwin_pkginfo()) + utils.MkdirAll(filepath.Join(depPath, name+".app", "Contents", "Resources")) + utils.Save(filepath.Join(depPath, name+".app", "Contents", "Resources", "empty.lproj"), "") + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+"/.", filepath.Join(depPath, name+".app")) + + if utils.QT_NIX() { + /* + TODO: + no self containing deployments possible because + macdeployqt can't find qmlimportscanner and + icu libs are still partially linked against + the store libs after macdeployqt + */ + + //workaround to make bundled applications start when they are double clicked from within the finder + os.Rename(filepath.Join(depPath, name+".app", "Contents", "MacOS", name), filepath.Join(depPath, name+".app", "Contents", "MacOS", name+"_bin")) + utils.SaveExec(filepath.Join(depPath, name+".app", "Contents", "MacOS", name), darwin_nix_script(name)) + break + } + + dep := exec.Command(utils.ToolPath("macdeployqt", target)) + dep.Args = append(dep.Args, filepath.Join(depPath, name+".app"), "-qmldir="+path) + dep.Dir = filepath.Dir(dep.Path) + utils.RunCmd(dep, fmt.Sprintf("deploy for %v on %v", target, runtime.GOOS)) + + case "linux", "rpi1", "rpi2", "rpi3": + defer func() { + filepath.Walk(depPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + if strings.HasPrefix(filepath.Base(path), "lib") { + utils.RunCmd(exec.Command("strip", "-s", path), "strip binaries on linux") + } + return nil + }) + }() + + //copy default assets + if target != "linux" || name == "lib" { + utils.SaveExec(filepath.Join(depPath, fmt.Sprintf("%v.sh", name)), linux_sh(target, name)) + } + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+"/.", depPath) + + //TODO: --> + { + if utils.QT_PKG_CONFIG() { + break + } + + libDir := "lib" + if name == libDir { + libDir = "libs" + } + utils.MkdirAll(filepath.Join(depPath, libDir)) + + var ( + libraryPath = strings.TrimSpace(utils.RunCmd(exec.Command(utils.ToolPath("qmake", target), "-query", "QT_INSTALL_LIBS"), fmt.Sprintf("query lib path for %v on %v", target, runtime.GOOS))) + lddPath = "ldd" + lddExtra string + lddOutput string + usesWebEngine bool + usesQml bool + ) + + if strings.HasPrefix(target, "rpi") { + //libraryPath = fmt.Sprintf("%v/%v/%v/lib/", utils.QT_DIR(), utils.QT_VERSION_MAJOR(), target) + lddPath = fmt.Sprintf("%v/arm-bcm2708/%v/bin/arm-linux-gnueabihf-ldd", utils.RPI_TOOLS_DIR(), utils.RPI_COMPILER()) + lddExtra = "--root=/" + lddOutput = utils.RunCmd(exec.Command(lddPath, lddExtra, filepath.Join(depPath, name)), fmt.Sprintf("ldd binary for %v on %v", target, runtime.GOOS)) + } else { + lddOutput = utils.RunCmd(exec.Command(lddPath, filepath.Join(depPath, name)), fmt.Sprintf("ldd binary for %v on %v", target, runtime.GOOS)) + } + + for _, dep := range strings.Split(lddOutput, "\n") { + if strings.Contains(dep, "libQt5") || strings.Contains(dep, "libicu") { + var libName string + + if strings.HasPrefix(target, "rpi") { + libName = strings.TrimSpace(strings.Replace(strings.Split(dep, "=>")[0], "not found", "", -1)) + } else { + if libraryPath == "" { + libraryPath, libName = filepath.Split(strings.Split(dep, " ")[2]) + } else { + _, libName = filepath.Split(strings.Split(dep, " ")[2]) + } + } + + if utils.ExistsFile(filepath.Join(libraryPath, libName)) { + utils.RunCmd(exec.Command("cp", "-L", strings.TrimSuffix(filepath.Join(libraryPath, libName), ".5"), filepath.Join(depPath, libDir, libName)), fmt.Sprintf("copy %v for %v on %v", libName, target, runtime.GOOS)) + } + + if strings.Contains(dep, "WebEngine") || strings.Contains(dep, "WebView") { + usesWebEngine = true + } + if strings.Contains(dep, "Quick") || strings.Contains(dep, "Qml") { + usesQml = true + } + } + } + + libs := []string{"DBus", "XcbQpa", "Quick", "Widgets", "EglDeviceIntegration", "EglFsKmsSupport", "OpenGL", "WaylandClient", "WaylandCompositor", "QuickControls2", "QuickTemplates2", "QuickWidgets", "QuickParticles", "CLucene", "Concurrent", "Svg", "MultimediaGstTools"} + if usesQml { + libs = append(libs, []string{"3DCore", "3DExtras", "3DInput", "3DLogic", "3DQuick", "3DQuickExtras", "3DQuickInput", "3DQuickRender", "3DRender", "Gamepad"}...) + } + if usesWebEngine { + libs = append(libs, []string{"WebEngine", "WebEngineCore", "WebChannel", "Positioning"}...) + } + for _, libName := range libs { + if utils.ExistsFile(filepath.Join(libraryPath, fmt.Sprintf("libQt5%v.so", libName))) { + utils.RunCmd(exec.Command("cp", "-L", filepath.Join(libraryPath, fmt.Sprintf("libQt5%v.so", libName)), filepath.Join(depPath, libDir, fmt.Sprintf("libQt5%v.so.5", libName))), fmt.Sprintf("copy %v for %v on %v", libName, target, runtime.GOOS)) + } + } + if utils.ExistsFile(filepath.Join(libraryPath, "libqgsttools_p.so.1.0.0")) { + utils.RunCmd(exec.Command("cp", "-L", filepath.Join(libraryPath, "libqgsttools_p.so.1.0.0"), filepath.Join(depPath, libDir, "libqgsttools_p.so.1")), fmt.Sprintf("copy libqgsttools_p.so.1 for %v on %v", target, runtime.GOOS)) + } + + libraryPath = filepath.Dir(libraryPath) + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "qml/"), depPath), fmt.Sprintf("copy qml dir for %v on %v", target, runtime.GOOS)) + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "plugins/"), depPath), fmt.Sprintf("copy plugins dir for %v on %v", target, runtime.GOOS)) + //TODO: use rsync with exclude instead ... + filepath.Walk(depPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + if filepath.Ext(info.Name()) == ".debug" || filepath.Ext(info.Name()) == ".qmlc" || filepath.Ext(info.Name()) == ".jsc" { + utils.RemoveAll(path) + } + return nil + }) + + if usesWebEngine { + utils.RunCmd(exec.Command("cp", filepath.Join(libraryPath, "libexec", "QtWebEngineProcess"), depPath), fmt.Sprintf("copy QtWebEngineProcess for %v on %v", target, runtime.GOOS)) + var fileList, err = ioutil.ReadDir(filepath.Join(libraryPath, "resources")) + if err != nil { + utils.Log.WithError(err).Error("failed to read resource folder") + } + for _, file := range fileList { + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "resources", file.Name()), depPath), fmt.Sprintf("copy resource %v for %v on %v", file.Name(), target, runtime.GOOS)) + } + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "translations/qtwebengine_locales/"), depPath), fmt.Sprintf("copy qtwebengine_locales dir for %v on %v", target, runtime.GOOS)) + } + + //patch QtCore path + pPath := "." + fn := filepath.Join(depPath, "/lib/", "libQt5Core.so.5") + data, err := ioutil.ReadFile(fn) + if err != nil { + utils.Log.WithError(err).Warn("couldn't find", fn) + break + } + + prefPath := "qt_prfxpath=" + + start := bytes.Index(data, []byte(prefPath)) + if start == -1 { + break + } + + end := bytes.IndexByte(data[start:], byte(0)) + if end == -1 { + break + } + + rep := append([]byte(prefPath), []byte(pPath)...) + if lendiff := end - len(rep); lendiff < 0 { + end -= lendiff + } else { + rep = append(rep, bytes.Repeat([]byte{0}, lendiff)...) + } + data = bytes.Replace(data, data[start:start+end], rep, -1) + + if err := ioutil.WriteFile(fn, data, 0644); err != nil { + utils.Log.WithError(err).Warn("couldn't patch", fn) + } else { + utils.Log.Debug("patched", fn) + } + } + //<-- + + case "windows": + + //TODO: --> + switch { + case runtime.GOOS != target: + if utils.QT_MXE_STATIC() { + break + } + + var libraryPath = filepath.Join(utils.QT_MXE_DIR(), "usr", utils.QT_MXE_TRIPLET(), "bin") + for _, d := range []string{"libbz2", "libfreetype-6", "libglib-2.0-0", "libharfbuzz-0", "libiconv-2", "libintl-8", "libpcre-1", "libpcre16-0", "libpng16-16", "libstdc++-6", "libwinpthread-1", "zlib1", "libgraphite2", "libicudt62", "libicuin62", "libicuuc62", "libeay32", "ssleay32", "libcrypto-1_1-x64", "libpcre2-16-0", "libssl-1_1-x64"} { + utils.RunCmdOptional(exec.Command("cp", filepath.Join(libraryPath, fmt.Sprintf("%v.dll", d)), depPath), fmt.Sprintf("copy %v for %v on %v", d, target, runtime.GOOS)) + } + for _, d := range []string{"libjasper-1", "libjpeg-9", "libmng-2", "libtiff-5", "libwebp-5", "liblcms2-2", "liblzma-5", "libwebpdemux-1"} { + utils.RunCmdOptional(exec.Command("cp", filepath.Join(libraryPath, fmt.Sprintf("%v.dll", d)), depPath), fmt.Sprintf("copy %v for %v on %v", d, target, runtime.GOOS)) + } + + var gccDep = "libgcc_s_sjlj-1" + if utils.QT_MXE_ARCH() == "amd64" { + gccDep = "libgcc_s_seh-1" + } + utils.RunCmdOptional(exec.Command("cp", filepath.Join(libraryPath, fmt.Sprintf("%v.dll", gccDep)), depPath), fmt.Sprintf("copy %v for %v on %v", gccDep, target, runtime.GOOS)) + + libraryPath = filepath.Join(utils.QT_MXE_DIR(), "usr", utils.QT_MXE_TRIPLET(), "qt5") + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "qml/")+"/.", depPath), fmt.Sprintf("copy qml dir for %v on %v", target, runtime.GOOS)) + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "plugins/")+"/.", depPath), fmt.Sprintf("copy plugins dir for %v on %v", target, runtime.GOOS)) + + libraryPath = filepath.Join(utils.QT_MXE_DIR(), "usr", utils.QT_MXE_TRIPLET(), "qt5", "bin") + var output = utils.RunCmd(exec.Command(utils.QT_MXE_BIN("objdump"), "-x", filepath.Join(depPath, name+".exe")), fmt.Sprintf("objdump binary for %v on %v", target, runtime.GOOS)) + for lib, deps := range parser.LibDeps { + if strings.Contains(output, lib) && lib != parser.MOC { + for _, lib := range append(deps, lib) { + if utils.ExistsFile(filepath.Join(libraryPath, fmt.Sprintf("Qt5%v.dll", lib))) && !utils.ExistsFile(filepath.Join(depPath, fmt.Sprintf("Qt5%v.dll", lib))) { + utils.RunCmd(exec.Command("cp", filepath.Join(libraryPath, fmt.Sprintf("Qt5%v.dll", lib)), depPath), fmt.Sprintf("copy %v for %v on %v", lib, target, runtime.GOOS)) + } + } + } + } + for _, d := range []string{"Qt5OpenGL", "Qt5Quick", "Qt5QuickControls2", "Qt5QuickTemplates2"} { + utils.RunCmdOptional(exec.Command("cp", filepath.Join(libraryPath, fmt.Sprintf("%v.dll", d)), depPath), fmt.Sprintf("copy %v for %v on %v", d, target, runtime.GOOS)) + } + + case utils.QT_MSYS2(): + if utils.QT_MSYS2_STATIC() { + break + } + + paths := make([]string, 0) + // make windeployqt run correctly + paths = append(paths, filepath.Join(utils.QT_MSYS2_DIR(), "bin")) + paths = append(paths, os.Getenv("PATH")) + os.Setenv("PATH", strings.Join(paths, ";")) + + copyCmd := "xcopy" + if utils.MSYSTEM() != "" { + copyCmd = "cp" + } + + deploy := exec.Command(filepath.Join(utils.QT_MSYS2_DIR(), "bin", "windeployqt")) + deploy.Args = append(deploy.Args, "--verbose=2", "--force", fmt.Sprintf("--qmldir=%v", path), filepath.Join(depPath, name+".exe")) + utils.RunCmd(deploy, fmt.Sprintf("depoy %v on %v", target, runtime.GOOS)) + + var libraryPath = filepath.Join(utils.QT_MSYS2_DIR(), "bin") + for _, d := range []string{"libbz2-1", "libfreetype-6", "libglib-2.0-0", "libharfbuzz-0", "libiconv-2", "libintl-8", "libpcre-1", "libpcre16-0", "libpng16-16", "libstdc++-6", "libwinpthread-1", "zlib1", "libgraphite2", "libicudt62", "libicuin62", "libicuuc62", "libeay32", "ssleay32", "libcrypto-1_1", "libpcre2-16-0", "libssl-1_1"} { + utils.RunCmdOptional(exec.Command(copyCmd, filepath.Join(libraryPath, fmt.Sprintf("%v.dll", d)), depPath), fmt.Sprintf("copy %v for %v on %v", d, target, runtime.GOOS)) + } + + var gccDep = "libgcc_s_dw2-1" + if utils.QT_MSYS2_ARCH() == "amd64" { + gccDep = "libgcc_s_seh-1" + } + + utils.RunCmdOptional(exec.Command(copyCmd, filepath.Join(libraryPath, fmt.Sprintf("%v.dll", gccDep)), depPath), fmt.Sprintf("copy %v for %v on %v", gccDep, target, runtime.GOOS)) + + libraryPath = filepath.Join(utils.QT_MSYS2_DIR(), "share", "qt5") + if utils.MSYSTEM() != "" { + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "qml/")+"/.", depPath), fmt.Sprintf("copy qml dir for %v on %v", target, runtime.GOOS)) + utils.RunCmd(exec.Command("cp", "-R", filepath.Join(libraryPath, "plugins/")+"/.", depPath), fmt.Sprintf("copy plugins dir for %v on %v", target, runtime.GOOS)) + } else { + utils.RunCmd(exec.Command("xcopy", "/S", "/Y", filepath.Join(libraryPath, "qml/"), depPath), fmt.Sprintf("copy qml dir for %v on %v", target, runtime.GOOS)) + utils.RunCmd(exec.Command("xcopy", "/S", "/Y", filepath.Join(libraryPath, "plugins/"), depPath), fmt.Sprintf("copy plugins dir for %v on %v", target, runtime.GOOS)) + } + + libraryPath = filepath.Join(utils.QT_MSYS2_DIR(), "bin") + var output = utils.RunCmd(exec.Command(filepath.Join(utils.QT_MSYS2_DIR(), "bin", "objdump"), "-x", filepath.Join(depPath, name+".exe")), fmt.Sprintf("objdump binary for %v on %v", target, runtime.GOOS)) + for lib, deps := range parser.LibDeps { + if strings.Contains(output, lib) && lib != parser.MOC { + for _, lib := range append(deps, lib) { + if utils.ExistsFile(filepath.Join(libraryPath, fmt.Sprintf("Qt5%v.dll", lib))) && !utils.ExistsFile(filepath.Join(depPath, fmt.Sprintf("Qt5%v.dll", lib))) { + if utils.MSYSTEM() != "" { + utils.RunCmd(exec.Command(copyCmd, filepath.Join(libraryPath, fmt.Sprintf("Qt5%v.dll", lib)), depPath), fmt.Sprintf("copy %v for %v on %v", lib, target, runtime.GOOS)) + } else { + utils.RunCmd(exec.Command("xcopy", "/Y", filepath.Join(libraryPath, fmt.Sprintf("Qt5%v.dll", lib)), depPath), fmt.Sprintf("copy %v for %v on %v", lib, target, runtime.GOOS)) + } + } + } + } + } + + deps := []string{"Qt5OpenGL", "Qt5Quick", "Qt5QuickControls2", "Qt5QuickTemplates2"} + if utils.QT_WEBKIT() { + deps = append(deps, []string{"libjpeg-8", "libsqlite3-0", "libwebp-7", "libxml2-2", "liblzma-5", "libxslt-1"}...) + } + for _, lib := range deps { + if utils.MSYSTEM() != "" { + utils.RunCmd(exec.Command(copyCmd, filepath.Join(libraryPath, fmt.Sprintf("%v.dll", lib)), depPath), fmt.Sprintf("copy %v for %v on %v", lib, target, runtime.GOOS)) + } else { + utils.RunCmd(exec.Command("xcopy", "/Y", filepath.Join(libraryPath, fmt.Sprintf("%v.dll", lib)), depPath), fmt.Sprintf("copy %v for %v on %v", lib, target, runtime.GOOS)) + } + } + + var walkFn = func(path string, info os.FileInfo, err error) error { + if strings.HasSuffix(info.Name(), "d.dll") { + utils.RemoveAll(path) + } + return nil + } + filepath.Walk(depPath, walkFn) + + default: + //copy default assets + //TODO: windres icon + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets, depPath) + + if utils.QT_WEBKIT() { + libraryPath := filepath.Dir(utils.ToolPath("qmake", target)) + output := utils.RunCmd(exec.Command(filepath.Join("objdump"), "-x", filepath.Join(depPath, name+".exe")), fmt.Sprintf("objdump binary for %v on %v", target, runtime.GOOS)) + for lib, deps := range parser.LibDeps { + if strings.Contains(output, lib) && lib == "WebKit" { + for _, lib := range append(deps, lib) { + for _, pref := range []string{"lib", ""} { + libName := filepath.Join(libraryPath, fmt.Sprintf("%vQt5%v.dll", pref, lib)) + if utils.ExistsFile(libName) { + copy(libName, depPath) + } + } + } + } + } + + for _, lib := range []string{"icudt57", "icuin57", "icuuc57", "libxml2-2", "libxslt-1", "Qt5MultimediaWidgets", "Qt5OpenGL", "Qt5PrintSupport"} { + copy(filepath.Join(libraryPath, lib+".dll"), depPath) + } + } + + dep := exec.Command(utils.ToolPath("windeployqt", target)) + dep.Args = append(dep.Args, "--verbose=2", "--force", fmt.Sprintf("--qmldir=%v", path), filepath.Join(depPath, name+".exe")) + utils.RunCmd(dep, fmt.Sprintf("deploy for %v on %v", target, runtime.GOOS)) + } + //<-- + + case "android", "android-emulator": + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+string(filepath.Separator)+".", filepath.Join(depPath, "build")) + + //wrap exported go main inside c main + env, _, _, _ := cmd.BuildEnv(target, name, depPath) + compiler := env["CXX"] + + wrapper := filepath.Join(depPath, "c_main_wrapper.cpp") + utils.Save(wrapper, "#include \"libgo_base.h\"\nint main(int argc, char *argv[]) { go_main_wrapper(argc, argv); }") + cmd := exec.Command(compiler, "c_main_wrapper.cpp", "-o", filepath.Join(depPath, "libgo.so"), "-I../..", "-L.", "-lgo_base", "-Wl,-soname,libgo.so", "-shared") + if target == "android-emulator" { + cmd = exec.Command(compiler, "c_main_wrapper.cpp", "-o", filepath.Join(depPath, "libgo.so"), "-I../..", "-L.", "-lgo_base", "-Wl,-soname,libgo.so", "-shared") + } + cmd.Args = append(cmd.Args, strings.Split(env["CGO_CPPFLAGS"], " ")...) + cmd.Args = append(cmd.Args, "-I"+filepath.Join(utils.ANDROID_NDK_DIR(), "sysroot", "usr", "include")) + cmd.Args = append(cmd.Args, strings.Split(env["CGO_LDFLAGS"], " ")...) + cmd.Dir = depPath + utils.RunCmd(cmd, fmt.Sprintf("compile wrapper for %v on %v", target, runtime.GOOS)) + utils.RemoveAll(wrapper) + + strip := exec.Command(filepath.Join(filepath.Dir(compiler), "llvm-strip"), "--strip-all", "libgo.so") + strip.Dir = depPath + utils.RunCmd(strip, fmt.Sprintf("strip binary for %v on %v", target, runtime.GOOS)) + + libPath := filepath.Join(depPath, "build", "libs", "armeabi-v7a") + if target == "android-emulator" { + libPath = filepath.Join(depPath, "build", "libs", "x86") + } + utils.MkdirAll(libPath) + + if utils.QT_VAGRANT() { + libPath = strings.Replace(libPath, "C:\\media\\sf_GOPATH", "C:\\media\\UNC\\vboxsrv\\media_sf_GOPATH", -1) + utils.RemoveAll(libPath) + utils.MkdirAll(libPath) + copy(filepath.Join(depPath, "libgo.so"), filepath.Join(libPath, "libgo.so")) + copy(filepath.Join(depPath, "libgo_base.so"), filepath.Join(libPath, "libgo_base.so")) + utils.RemoveAll(filepath.Join(depPath, "libgo.so")) + utils.RemoveAll(filepath.Join(depPath, "libgo_base.so")) + } else { + os.Rename(filepath.Join(depPath, "libgo.so"), filepath.Join(libPath, "libgo.so")) + os.Rename(filepath.Join(depPath, "libgo_base.so"), filepath.Join(libPath, "libgo_base.so")) + } + + //trick androiddeployqt into checking dependencies from libgo_base.so + copy(filepath.Join(libPath, "libgo_base.so"), depPath) + copy(filepath.Join(libPath, "libgo_base.so"), filepath.Join(depPath, "libgo.so")) + + utils.Save(filepath.Join(depPath, "android-libgo.so-deployment-settings.json"), android_config(target, path, depPath)) + + dep := exec.Command(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "android_armv7", "bin", "androiddeployqt")) + dep.Dir = filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "android_armv7", "bin") + if target == "android-emulator" { + dep := exec.Command(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "android_x86", "bin", "androiddeployqt")) + dep.Dir = filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "android_x86", "bin") + } + dep.Env = append(dep.Env, "JAVA_HOME="+utils.JDK_DIR()) + dep.Args = append(dep.Args, + "--input", filepath.Join(depPath, "android-libgo.so-deployment-settings.json"), + "--output", filepath.Join(depPath, "build"), + "--deployment", "bundled", + "--android-platform", "android-28", + "--jdk", utils.JDK_DIR(), + "--gradle", + "--verbose", + ) + if !utils.QT_DEBUG_QML() { + dep.Args = append(dep.Args, "--no-gdbserver") + } + + if utils.ExistsFile(filepath.Join(path, target, name+".jks")) { + dep.Args = append(dep.Args, + "--sign", filepath.Join(path, target, name+".jks"), strings.TrimSpace(utils.Load(filepath.Join(path, target, "jks_alias"))), + "--storepass", strings.TrimSpace(utils.Load(filepath.Join(path, target, "jks_pass")))) + } + + if runtime.GOOS == "windows" { + //TODO: --> + utils.SaveExec(filepath.Join(depPath, "build.bat"), fmt.Sprintf("set JAVA_HOME=%v\r\n%v", utils.JDK_DIR(), strings.Join(dep.Args, " "))) + utils.RunCmd(exec.Command(filepath.Join(depPath, "build.bat")), fmt.Sprintf("deploy for %v on %v", target, runtime.GOOS)) + utils.RemoveAll(filepath.Join(depPath, "build.bat")) + //<-- + } else { + utils.RunCmd(dep, fmt.Sprintf("deploy for %v on %v", target, runtime.GOOS)) + } + + if utils.QT_VAGRANT() { + depPathUNC := strings.Replace(depPath, "C:\\media\\sf_GOPATH", "C:\\media\\UNC\\vboxsrv\\media_sf_GOPATH", -1) + if utils.ExistsFile(filepath.Join(path, target, name+".jks")) { + copy(filepath.Join(depPathUNC, "build", "build", "outputs", "apk", "release", "build-release-signed.apk"), depPath) + } else { + copy(filepath.Join(depPathUNC, "build", "build", "outputs", "apk", "debug", "build-debug.apk"), depPath) + } + } else { + if utils.ExistsFile(filepath.Join(path, target, name+".jks")) { + copy(filepath.Join(depPath, "build", "build", "outputs", "apk", "release", "build-release-signed.apk"), depPath) + } else { + copy(filepath.Join(depPath, "build", "build", "outputs", "apk", "debug", "build-debug.apk"), depPath) + } + } + + case "ios", "ios-simulator": + + //copy default assets + buildPath := filepath.Join(depPath, "build") + utils.MkdirAll(filepath.Join(buildPath, "project.xcodeproj")) + utils.MkdirAll(filepath.Join(buildPath, "Images.xcassets", "AppIcon.appiconset")) + utils.Save(filepath.Join(buildPath, "Info.plist"), ios_plist(name)) + utils.Save(filepath.Join(buildPath, "Images.xcassets", "AppIcon.appiconset", "Contents.json"), ios_appicon()) + utils.Save(filepath.Join(buildPath, "LaunchScreen.xib"), ios_launchscreen(name)) + utils.Save(filepath.Join(buildPath, "project.xcodeproj", "project.pbxproj"), ios_xcodeproject()) + copy(filepath.Join(utils.QT_DIR(), utils.QT_VERSION_MAJOR(), "ios", "mkspecs", "macx-ios-clang", "Default-568h@2x.png"), buildPath) + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+"/.", buildPath) + + var t string + switch target { + case "ios": + t = "arm64" + case "ios-simulator": + t = "x86_64" + } + + utils.Save(filepath.Join(depPath, "c_main_wrapper_"+t+".cpp"), ios_c_main_wrapper()) + rcc.ResourceNames = make(map[string]string) + cmd := exec.Command("xcrun", "clang++", "c_main_wrapper_"+t+".cpp", target+"_plugin_import.cpp") + newArgs := templater.GetiOSClang(target, t, depPath) + if utils.ExistsFile(filepath.Join(depPath, target+"_qml_plugin_import.cpp")) { + cmd.Args = append(cmd.Args, target+"_qml_plugin_import.cpp") + } + cmd.Args = append(cmd.Args, "-o", "build/main", "-u", "_qt_registerPlatformPlugin", "-Wl,-e,_qt_main_wrapper", "-I../..", "-L.", "-lgo") + cmd.Dir = depPath + cmd.Args = append(cmd.Args, newArgs...) + utils.RunCmd(cmd, fmt.Sprintf("compile wrapper for %v (%v) on %v", target, t, runtime.GOOS)) + + strip := exec.Command("strip", "main") + strip.Dir = filepath.Join(depPath, "build") + utils.RunCmd(strip, fmt.Sprintf("strip binary for %v (%v) on %v", target, t, runtime.GOOS)) + + //run xcodebuild + utils.RunCmd(exec.Command("xcrun", "xcodebuild", "clean", "build", "CODE_SIGN_IDENTITY=", "CODE_SIGNING_REQUIRED=NO", "CONFIGURATION_BUILD_DIR="+depPath, "-configuration", "Release", "-project", filepath.Join(depPath, "build", "project.xcodeproj")), fmt.Sprintf("deploy for %v on %v", target, runtime.GOOS)) + + case "sailfish", "sailfish-emulator": + + //copy default assets + utils.MkdirAll(filepath.Join(depPath, "rpm")) + utils.Save(filepath.Join(depPath, "rpm", name+".spec"), sailfish_spec(name)) + utils.Save(filepath.Join(depPath, name+".desktop"), sailfish_desktop(name)) + if utils.QT_SAILFISH() { + copy("/srv/mer/targets/SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-i486/usr/share/themes/sailfish-default/meegotouch/z1.0/icons/icon-launcher-default.png", filepath.Join(depPath, fmt.Sprintf("harbour-%v.png", name))) + } else { + copy(filepath.Join(utils.SAILFISH_DIR(), "tutorials", "stocqt", "stocqt.png"), filepath.Join(depPath, fmt.Sprintf("harbour-%v.png", name))) + } + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+"/.", depPath) + + if utils.QT_SAILFISH() { + utils.RemoveAll(filepath.Join("/home", "user", target)) + copy(strings.Replace(depPath, "\\", "/", -1), filepath.Join("/home", "user", target)) + + arch, template := "i486", "i486-meego-linux-gnu" + if target == "sailfish" { + arch, template = "armv7hl", "armv7hl-meego-linux" + } + + pack := exec.Command("mb2", "-t", template, "build") + pack.Dir = filepath.Join("/home", "user", target) + utils.RunCmd(pack, fmt.Sprintf("deploy for %v (%v) on %v", target, arch, runtime.GOOS)) + + copy(filepath.Join("/home", "user", target, "RPMS")+"/.", strings.Replace(depPath, "\\", "/", -1)) + } else { + err := sailfish_ssh("2222", "mersdk", "cd", "/home/mersdk", "&&", "rm", "-R", target) + if err != nil { + utils.Log.WithError(err).Warnf("failed to cleanup for %v on %v", target, runtime.GOOS) + } + + err = sailfish_ssh("2222", "mersdk", "cd", strings.Replace(strings.Replace(path, utils.MustGoPath(), "/media/sf_GOPATH/", -1)+"/deploy", "\\", "/", -1), "&&", "cp", "-R", target, "/home/mersdk") + if err != nil { + utils.Log.WithError(err).Panicf("failed to copy project for %v on %v", target, runtime.GOOS) + } + + arch := "i486" + if target == "sailfish" { + arch = "armv7hl" + } + err = sailfish_ssh("2222", "mersdk", "cd", "/home/mersdk/"+target, "&&", "mb2", "-t", "SailfishOS-"+utils.QT_SAILFISH_VERSION()+"-"+arch, "build") + if err != nil { + utils.Log.WithError(err).Errorf("failed to deploy for %v (%v) on %v", target, arch, runtime.GOOS) + } + + err = sailfish_ssh("2222", "mersdk", "cd", "/home/mersdk/"+target+"/RPMS", "&&", "cp", "*", strings.Replace(strings.Replace(depPath, utils.MustGoPath(), "/media/sf_GOPATH/", -1), "\\", "/", -1)) + if err != nil { + utils.Log.WithError(err).Panicf("failed to receive project for %v on %v", target, runtime.GOOS) + } + } + + case "ubports": + + //copy default assets + copy("/usr/share/icons/suru/apps/sources/placeholder-app-icon.svg", filepath.Join(depPath, "logo.svg")) + utils.Save(filepath.Join(depPath, "manifest.json"), ubports_manifest(name)) + utils.Save(filepath.Join(depPath, fmt.Sprintf("%v.desktop", name)), ubports_desktop(name)) + utils.Save(filepath.Join(depPath, fmt.Sprintf("%v.apparmor", name)), ubports_apparmor()) + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+"/.", depPath) + + click := exec.Command("click", "build", "--no-validate", depPath) + click.Dir = depPath + utils.RunCmd(click, fmt.Sprintf("deploy for %v (%v) on %v", target, utils.QT_UBPORTS_ARCH(), runtime.GOOS)) + + case "js", "wasm": + + //copy default assets + copy(filepath.Join(utils.QT_QMAKE_DIR(), "..", "plugins", "platforms", "wasm_shell.html"), filepath.Join(depPath, "index.html")) + copy(filepath.Join(utils.QT_QMAKE_DIR(), "..", "plugins", "platforms", "qtloader.js"), depPath) + copy(filepath.Join(utils.QT_QMAKE_DIR(), "..", "plugins", "platforms", "qtlogo.svg"), depPath) + if parser.UseWasm() { + copy(filepath.Join(runtime.GOROOT(), "misc", "wasm", "wasm_exec.js"), filepath.Join(depPath, "go.js")) + } + + //patch default assets + index := utils.Load(filepath.Join(depPath, "index.html")) + index = strings.Replace(index, "APPNAME", "main", -1) + utils.Save(filepath.Join(depPath, "index.html"), strings.Replace(index, " ", " \n ", -1)) + + if parser.UseWasm() { + utils.Save(filepath.Join(depPath, "go.js"), strings.Replace(utils.Load(filepath.Join(depPath, "go.js")), "})();", wasm_js(), -1)) + } else { + gojs := utils.Load(filepath.Join(depPath, "go.js")) + gojs = strings.Replace(gojs, "(function() {", "Module._goMain = function() {", -1) + utils.Save(filepath.Join(depPath, "go.js"), strings.Replace(gojs, "}).call(this);", "};", -1)) + } + + //copy custom assets + assets := filepath.Join(path, target) + utils.MkdirAll(assets) + copy(assets+"/.", depPath) + + if fast { + break + } + + utils.Save(filepath.Join(depPath, "c_main_wrapper_js.cpp"), js_c_main_wrapper(target)) + env, _, _, _ := cmd.BuildEnv(target, "", "") + cmd := exec.Command(filepath.Join(env["EMSCRIPTEN"], "em++"), "c_main_wrapper_js.cpp", target+".js_plugin_import.cpp") + cmd.Dir = depPath + + for rccFile := range rcc.ResourceNames { + cmd.Args = append(cmd.Args, rccFile) + } + rcc.ResourceNames = make(map[string]string) + + for mocFile := range moc.ResourceNames { + cmd.Args = append(cmd.Args, mocFile) + } + moc.ResourceNames = make(map[string]string) + + //TODO: use "go list" deps instead ? and get rid of "build_static" -> + //also re-enable GOCACHE support once done + for _, l := range parser.LibDeps["build_static"] { + for _, ml := range parser.GetLibs() { + if strings.ToLower(l) == strings.ToLower(ml) { + cmd.Args = append(cmd.Args, utils.GoQtPkgPath(strings.ToLower(l), strings.ToLower(l)+"-minimal.cpp")) + break + } + } + } + + for _, l := range parser.LibDeps[parser.MOC] { + for _, ml := range parser.GetLibs() { + if strings.ToLower(l) == strings.ToLower(ml) { + cmd.Args = append(cmd.Args, utils.GoQtPkgPath(strings.ToLower(l), strings.ToLower(l)+"-minimal.cpp")) + break + } + } + } + /* + if !utils.QT_FAT() { + tags = append(tags, "minimal") + } + if tagsCustom != "" { + tags = append(tags, strings.Split(tagsCustom, " ")...) + } + lcmd := utils.GoList("{{ join .Deps \"|\" }}", fmt.Sprintf("-tags=\"%v\"", strings.Join(tags, "\" \""))) + lcmd.Dir = path + for k, v := range env { + lcmd.Env = append(lcmd.Env, fmt.Sprintf("%v=%v", k, v)) + } + for _, l := range strings.Split(strings.TrimSpace(utils.RunCmd(lcmd, "go list deps")), "|") { + for _, ml := range parser.GetLibs() { + if strings.HasSuffix(strings.ToLower(l), "github.com/peterq/pan-light/qt/"+strings.ToLower(ml)) { + cmd.Args = append(cmd.Args, utils.GoQtPkgPath(strings.ToLower(ml), strings.ToLower(ml)+"-minimal.cpp")) + break + } + } + } + */ + //<- + + //TODO: check if minimal packages are stale and skip main.js rebuild this if they aren't + newArgs := templater.GetiOSClang(target, "", depPath) + if utils.ExistsFile(filepath.Join(depPath, target+".js_qml_plugin_import.cpp")) { + cmd.Args = append(cmd.Args, target+".js_qml_plugin_import.cpp") + } + cmd.Args = append(cmd.Args, newArgs...) + cmd.Args = append(cmd.Args, []string{"-o", "main.js"}...) + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + utils.RunCmd(cmd, fmt.Sprintf("compile wrapper for %v (%v) on %v", target, target, runtime.GOOS)) + + utils.RemoveAll(filepath.Join(depPath, "c_main_wrapper_js.cpp")) + utils.RemoveAll(filepath.Join(depPath, target+".js_plugin_import.cpp")) + utils.RemoveAll(filepath.Join(depPath, target+".js_qml_plugin_import.cpp")) + utils.RemoveAll(filepath.Join(depPath, "go.js.map")) + } + + if utils.QT_DOCKER() { + if idug, ok := os.LookupEnv("IDUG"); ok { + utils.RunCmd(exec.Command("chown", "-R", idug, path), "chown files to user") + } + } +} diff --git a/qt/tool-chain/cmd/deploy/deploy.go b/qt/tool-chain/cmd/deploy/deploy.go new file mode 100644 index 0000000..c2ad731 --- /dev/null +++ b/qt/tool-chain/cmd/deploy/deploy.go @@ -0,0 +1,98 @@ +package deploy + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/cmd/minimal" + "github.com/peterq/pan-light/qt/tool-chain/cmd/moc" + "github.com/peterq/pan-light/qt/tool-chain/cmd/rcc" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Deploy(mode, target, path string, docker bool, ldFlags, tags string, fast bool, device string, vagrant bool, vagrantsystem string, comply bool) { + utils.Log.WithField("mode", mode).WithField("target", target).WithField("path", path).WithField("docker", docker).WithField("ldFlags", ldFlags).WithField("fast", fast).WithField("comply", comply).Debug("running Deploy") + name := filepath.Base(path) + switch name { + case "lib", "plugins", "qml", + "audio", "bearer", "iconengines", "imageformats", "mediaservice", + "platforminputcontexts", "platforms", "playlistformats", "qmltooling", + "qt", "Qt", "QT", "styles", "translations": + name += "_project" + } + depPath := filepath.Join(path, "deploy", target) + + switch mode { + case "build", "test": + + if docker || vagrant { + args := []string{"qtdeploy", "-debug"} + if fast { + args = append(args, "-fast") + } + if comply { + args = append(args, "-comply") + } + if vagrantsystem == "docker" { + args = append(args, "-docker") + } + args = append(args, []string{"-ldflags=" + ldFlags, "-tags=" + tags, "build"}...) + + if docker { + cmd.Docker(args, target, path, false) + } else { + cmd.Vagrant(args, target, path, false, vagrantsystem) + } + break + } + + if !fast { + err := os.RemoveAll(depPath) + if err != nil { + utils.Log.WithError(err).Panic("failed to remove deploy folder") + } + + if utils.UseGOMOD(path) { + if !utils.ExistsDir(filepath.Join(path, "vendor")) { + cmd := exec.Command("go", "mod", "vendor") + cmd.Dir = path + utils.RunCmd(cmd, "go mod vendor") + } + } + } + + if utils.ExistsDir(depPath + "_obj") { + utils.RemoveAll(depPath + "_obj") + } + + rcc.Rcc(path, target, tags, os.Getenv("QTRCC_OUTPUT_DIR")) + if !fast { + moc.Moc(path, target, tags, false, false) + } + + if ((!fast || utils.QT_STUB()) || ((target == "js" || target == "wasm") && (utils.QT_DOCKER() || utils.QT_VAGRANT()))) && !utils.QT_FAT() { + minimal.Minimal(path, target, tags) + } + + build(mode, target, path, ldFlags, tags, name, depPath, fast, comply) + + if !(fast || (utils.QT_DEBUG_QML() && target == runtime.GOOS)) || (target == "js" || target == "wasm") { + bundle(mode, target, path, name, depPath, tags, fast) + } else if fast { + switch target { + case "darwin": + if fn := filepath.Join(depPath, name+".app", "Contents", "Info.plist"); !utils.ExistsFile(fn) { + utils.Save(fn, darwin_plist(name)) + } + } + } + } + + if (mode == "run" || mode == "test") && !(fast && (target == "js" || target == "wasm")) { + run(target, name, depPath, device) + } +} diff --git a/qt/tool-chain/cmd/deploy/run.go b/qt/tool-chain/cmd/deploy/run.go new file mode 100644 index 0000000..ab10e56 --- /dev/null +++ b/qt/tool-chain/cmd/deploy/run.go @@ -0,0 +1,80 @@ +package deploy + +import ( + "fmt" + "os/exec" + "path/filepath" + "runtime" + "strings" + "time" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func run(target, name, depPath, device string) { + switch target { + case "android", "android-emulator": + if utils.ExistsFile(filepath.Join(depPath, "build-debug.apk")) { + exec.Command(filepath.Join(utils.ANDROID_SDK_DIR(), "platform-tools", "adb"), "install", "-r", filepath.Join(depPath, "build-debug.apk")).Start() + } else { + exec.Command(filepath.Join(utils.ANDROID_SDK_DIR(), "platform-tools", "adb"), "install", "-r", filepath.Join(depPath, "build-release-signed.apk")).Start() + } + + //TODO: parse manifest for ident and start app (+ logcat) + + case "ios-simulator": + if device == "" { + out, _ := exec.Command("xcrun", "instruments", "-s").Output() + lines := strings.Split(string(out), "iPhone") + device = strings.Split(strings.Split(string(out), "iPhone 8 ("+strings.Split(strings.Split(lines[len(lines)-1], "(")[1], ")")[0]+") [")[1], "]")[0] + } + go utils.RunCmdOptional(exec.Command("xcrun", "instruments", "-w", device), "start simulator") + time.Sleep(1 * time.Second) + utils.RunCmdOptional(exec.Command("xcrun", "simctl", "uninstall", "booted", filepath.Join(depPath, "main.app")), "uninstall old app") + utils.RunCmdOptional(exec.Command("xcrun", "simctl", "install", "booted", filepath.Join(depPath, "main.app")), "install new app") + utils.RunCmdOptional(exec.Command("xcrun", "simctl", "launch", "booted", strings.Replace(name, "_", "", -1)), "start app") //TODO: parse ident from plist + + case "darwin": + exec.Command("open", filepath.Join(depPath, fmt.Sprintf("%v.app", name))).Start() + + case "linux": + exec.Command(filepath.Join(depPath, name)).Start() + + case "windows": + if runtime.GOOS == target { + exec.Command(filepath.Join(depPath, name+".exe")).Start() + } else { + exec.Command("wine", filepath.Join(depPath, name+".exe")).Start() + } + + case "sailfish-emulator": + if utils.QT_SAILFISH() { + return + } + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "registervm", filepath.Join(utils.SAILFISH_DIR(), "emulator", "Sailfish OS Emulator", "Sailfish OS Emulator.vbox")), "register vm") + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "sharedfolder", "add", "Sailfish OS Emulator", "--name", "GOPATH", "--hostpath", utils.MustGoPath(), "--automount"), "mount GOPATH") + + if runtime.GOOS == "windows" { + utils.RunCmdOptional(exec.Command(filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "startvm", "Sailfish OS Emulator"), "start emulator") + } else { + utils.RunCmdOptional(exec.Command("nohup", filepath.Join(utils.VIRTUALBOX_DIR(), "vboxmanage"), "startvm", "Sailfish OS Emulator"), "start emulator") + } + + time.Sleep(10 * time.Second) + + err := sailfish_ssh("2223", "nemo", "sudo", "rpm", "-i", "--force", strings.Replace(strings.Replace(depPath, utils.MustGoPath(), "/media/sf_GOPATH/", -1)+"/*.rpm", "\\", "/", -1)) + if err != nil { + utils.Log.WithError(err).Errorf("failed to install %v for %v", name, target) + } + + err = sailfish_ssh("2223", "nemo", "nohup", "/usr/bin/harbour-"+name, ">", "/dev/null", "2>&1", "&") + if err != nil { + utils.Log.WithError(err).Errorf("failed to run %v for %v", name, target) + } + + case "js", "wasm": //TODO: REVIEW and use emscripten wrapper instead + if runtime.GOOS == "darwin" { + exec.Command("/Applications/Firefox Nightly.app/Contents/MacOS/firefox", filepath.Join(depPath, "index.html")).Start() + } + } +} diff --git a/qt/tool-chain/cmd/minimal/minimal.go b/qt/tool-chain/cmd/minimal/minimal.go new file mode 100644 index 0000000..58c6fef --- /dev/null +++ b/qt/tool-chain/cmd/minimal/minimal.go @@ -0,0 +1,392 @@ +package minimal + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + + "github.com/peterq/pan-light/qt/tool-chain/binding/converter" + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/binding/templater" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Minimal(path, target, tags string) { + if utils.UseGOMOD(path) { + if !utils.ExistsDir(filepath.Join(path, "vendor")) { + cmd := exec.Command("go", "mod", "vendor") + cmd.Dir = path + utils.RunCmd(cmd, "go mod vendor") + } + } + + env, tagsEnv, _, _ := cmd.BuildEnv(target, "", "") + scmd := utils.GoList("'{{.Stale}}':'{{.StaleReason}}'") + scmd.Dir = path + + tagsEnv = append(tagsEnv, "minimal") + + if tags != "" { + tagsEnv = append(tagsEnv, strings.Split(tags, " ")...) + } + scmd.Args = append(scmd.Args, fmt.Sprintf("-tags=\"%v\"", strings.Join(tagsEnv, "\" \""))) + + if target != runtime.GOOS { + scmd.Args = append(scmd.Args, []string{"-pkgdir", filepath.Join(utils.MustGoPath(), "pkg", fmt.Sprintf("%v_%v_%v", strings.Replace(target, "-", "_", -1), env["GOOS"], env["GOARCH"]))}...) + } + + for key, value := range env { + scmd.Env = append(scmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + + if out := utils.RunCmdOptional(scmd, fmt.Sprintf("go check stale for %v on %v", target, runtime.GOOS)); strings.Contains(out, "but available in build cache") || strings.Contains(out, "false") { + utils.Log.WithField("path", path).Debug("skipping already cached minimal") + return + } + + utils.Log.WithField("path", path).WithField("target", target).Debug("start Minimal") + + //TODO: cleanup state from moc for minimal first --> + for _, c := range parser.State.ClassMap { + if c.Module == parser.MOC || strings.HasPrefix(c.Module, "custom_") { + delete(parser.State.ClassMap, c.Name) + } + } + parser.LibDeps[parser.MOC] = make([]string, 0) + if target == "js" || target == "wasm" { //TODO: REVIEW + if parser.LibDeps["build_static"][0] == "Qml" { + parser.LibDeps["build_static"] = parser.LibDeps["build_static"][1:] + } + } else { + parser.LibDeps["build_static"] = []string{"Qml"} + } + //<-- + + wg := new(sync.WaitGroup) + wc := make(chan bool, 50) + + var files []string + fileMutex := new(sync.Mutex) + + allImports := append([]string{path}, cmd.GetImports(path, target, tags, 0, false, false)...) + wg.Add(len(allImports)) + for _, path := range allImports { + wc <- true + go func(path string) { + for _, path := range cmd.GetGoFiles(path, target, tags) { + if base := filepath.Base(path); strings.HasPrefix(base, "rcc_cgo") || strings.HasPrefix(base, "moc_cgo") { + continue + } + utils.Log.WithField("path", path).Debug("analyse for minimal") + file := utils.Load(path) + fileMutex.Lock() + files = append(files, file) + fileMutex.Unlock() + } + if target == "js" { //TODO: wasm as well + filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if err != nil || info.IsDir() { + return err + } + if filepath.Ext(path) == ".js" { + utils.Log.WithField("path", path).Debug("analyse js for minimal") + file := utils.Load(path) + fileMutex.Lock() + files = append(files, file) + fileMutex.Unlock() + } + return nil + }) + } + <-wc + wg.Done() + }(path) + } + wg.Wait() + + c := len(files) + utils.Log.Debugln("found", c, "files to analyze") + if c == 0 { + return + } + + if _, ok := parser.State.ClassMap["QObject"]; !ok { + parser.LoadModules() + } else { + utils.Log.Debug("modules already cached") + } + + //TODO: merge sailfish and asteroid + switch target { + case "sailfish", "sailfish-emulator": + for _, bl := range []string{"TestCase", "QQuickWidget", "QLatin1String", "QStringRef"} { + if c, ok := parser.State.ClassMap[bl]; ok { + c.Export = false + delete(parser.State.ClassMap, c.Name) + } + } + + for _, c := range parser.State.ClassMap { + since, err := strconv.ParseFloat(strings.TrimPrefix(c.Since, "Qt "), 64) + if err != nil { + continue + } + if since >= 5.3 || !parser.IsWhiteListedSailfishLib(strings.TrimPrefix(c.Module, "Qt")) { + c.Export = false + delete(parser.State.ClassMap, c.Name) + continue + } + + for _, f := range c.Functions { + since, err := strconv.ParseFloat(strings.TrimPrefix(f.Since, "Qt "), 64) + if err != nil { + continue + } + if since >= 5.3 { + f.Export = false + } + } + } + + case "asteroid": + for _, bl := range []string{"TestCase", "QQuickWidget"} { + if c, ok := parser.State.ClassMap[bl]; ok { + c.Export = false + delete(parser.State.ClassMap, c.Name) + } + } + + for _, c := range parser.State.ClassMap { + since, err := strconv.ParseFloat(strings.TrimPrefix(c.Since, "Qt "), 64) + if err != nil { + continue + } + if since >= 5.7 || !parser.IsWhiteListedSailfishLib(strings.TrimPrefix(c.Module, "Qt")) { + c.Export = false + delete(parser.State.ClassMap, c.Name) + continue + } + + for _, f := range c.Functions { + since, err := strconv.ParseFloat(strings.TrimPrefix(f.Since, "Qt "), 64) + if err != nil { + continue + } + if since >= 5.7 { + f.Export = false + } + } + } + + case "ios", "ios-simulator": + for _, bl := range []string{"QProcess", "QProcessEnvironment"} { + if c, ok := parser.State.ClassMap[bl]; ok { + c.Export = false + delete(parser.State.ClassMap, bl) + } + } + + case "rpi1", "rpi2", "rpi3": + if !utils.QT_RPI() { + break + } + for _, bl := range []string{"TestCase", "QQuickWidget"} { + if c, ok := parser.State.ClassMap[bl]; ok { + c.Export = false + delete(parser.State.ClassMap, c.Name) + } + } + + for _, c := range parser.State.ClassMap { + since, err := strconv.ParseFloat(strings.TrimPrefix(c.Since, "Qt "), 64) + if err != nil { + continue + } + if since >= 5.8 || !parser.IsWhiteListedRaspberryLib(strings.TrimPrefix(c.Module, "Qt")) { + c.Export = false + delete(parser.State.ClassMap, c.Name) + continue + } + + for _, f := range c.Functions { + since, err := strconv.ParseFloat(strings.TrimPrefix(f.Since, "Qt "), 64) + if err != nil { + continue + } + if since >= 5.8 { + f.Export = false + } + } + } + case "js", "wasm": + parser.State.ClassMap["QSvgWidget"].Export = true + } + + wg.Add(len(files)) + for _, f := range files { + go func(f string) { + for _, c := range parser.State.ClassMap { + if strings.Contains(f, c.Name) && + strings.Contains(f, fmt.Sprintf("github.com/peterq/pan-light/qt/%v", strings.ToLower(strings.TrimPrefix(c.Module, "Qt")))) { + exportClass(c, files) + } + } + wg.Done() + }(f) + } + wg.Wait() + + parser.State.ClassMap["QVariant"].Export = true + + //TODO: cleanup state + parser.State.Minimal = true + for _, m := range parser.GetLibs() { + if !parser.ShouldBuildForTarget(m, target) || + m == "AndroidExtras" || m == "Sailfish" { + continue + } + + utils.MkdirAll(utils.GoQtPkgPath(strings.ToLower(m))) + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+"-minimal.cpp"), templater.CppTemplate(m, templater.MINIMAL, target, "")) + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+"-minimal.h"), templater.HTemplate(m, templater.MINIMAL, "")) + utils.SaveBytes(utils.GoQtPkgPath(strings.ToLower(m), strings.ToLower(m)+"-minimal.go"), templater.GoTemplate(m, false, templater.MINIMAL, m, target, "")) + } + + for _, m := range parser.GetLibs() { + if !parser.ShouldBuildForTarget(m, target) || + m == "AndroidExtras" || m == "Sailfish" { + continue + } + + wg.Add(1) + go func(m string, libs []string) { + templater.CgoTemplateSafe(m, "", target, templater.MINIMAL, m, "", libs) + wg.Done() + }(m, parser.LibDeps[m]) + } + wg.Wait() + + parser.State.Minimal = false + for _, c := range parser.State.ClassMap { + c.Export = false + for _, f := range c.Functions { + f.Export = false + } + } +} + +func exportClass(c *parser.Class, files []string) { + c.Lock() + exp := c.Export + c.Unlock() + if exp { + return + } + c.Lock() + c.Export = true + c.Unlock() + + for _, file := range files { + for _, f := range c.Functions { + + switch { + case f.Virtual == parser.IMPURE, f.Virtual == parser.PURE, f.Meta == parser.SIGNAL, f.Meta == parser.SLOT: + for _, mode := range []string{parser.CONNECT, parser.DISCONNECT, ""} { + f.SignalMode = mode + if strings.Contains(file, converter.GoHeaderName(f)) { + exportFunction(f, files) + } + } + + default: + if f.Static { + if strings.Contains(file, converter.GoHeaderName(f)) { + exportFunction(f, files) + } + f.Static = false + if strings.Contains(file, converter.GoHeaderName(f)) { + exportFunction(f, files) + } + f.Static = true + } else { + if strings.Contains(file, converter.GoHeaderName(f)) { + exportFunction(f, files) + } + } + } + + if strings.HasPrefix(f.Name, "__") || f.Meta == parser.CONSTRUCTOR || + f.Meta == parser.DESTRUCTOR || f.Virtual == parser.PURE { + exportFunction(f, files) + } + + } + } + + for _, b := range c.GetAllBases() { + if c, ok := parser.State.ClassMap[b]; ok { + exportClass(c, files) + } + } +} + +func exportFunction(f *parser.Function, files []string) { + if f.Export { + return + } + f.Export = true + + for _, p := range f.Parameters { + if c, ok := parser.State.ClassMap[parser.CleanValue(p.Value)]; ok { + exportClass(c, files) + } + if parser.IsPackedList(p.Value) { + if c, ok := parser.State.ClassMap[parser.UnpackedList(p.Value)]; ok { + exportClass(c, files) + } + } + if parser.IsPackedMap(p.Value) { + key, value := parser.UnpackedMap(p.Value) + if c, ok := parser.State.ClassMap[key]; ok { + exportClass(c, files) + } + if c, ok := parser.State.ClassMap[value]; ok { + exportClass(c, files) + } + } + } + + if c, ok := parser.State.ClassMap[parser.CleanValue(f.Output)]; ok { + exportClass(c, files) + } + if parser.IsPackedList(f.Output) { + if c, ok := parser.State.ClassMap[parser.UnpackedList(f.Output)]; ok { + exportClass(c, files) + } + } + if parser.IsPackedMap(f.Output) { + key, value := parser.UnpackedMap(f.Output) + if c, ok := parser.State.ClassMap[key]; ok { + exportClass(c, files) + } + if c, ok := parser.State.ClassMap[value]; ok { + exportClass(c, files) + } + } + + if c, ok := parser.State.ClassMap[parser.CleanValue(f.Output)]; ok && f.Virtual == parser.PURE { + for _, f := range c.Functions { + if f.Meta == parser.CONSTRUCTOR && len(f.Parameters) == 0 { + exportFunction(f, files) + } + } + } +} diff --git a/qt/tool-chain/cmd/setup/check.go b/qt/tool-chain/cmd/setup/check.go new file mode 100644 index 0000000..d40b8a0 --- /dev/null +++ b/qt/tool-chain/cmd/setup/check.go @@ -0,0 +1,153 @@ +package setup + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Check(target string, docker, vagrant bool) { + utils.Log.Infof("running: 'qtsetup check %v' [docker=%v] [vagrant=%v]", target, docker, vagrant) + if docker { + if _, err := exec.LookPath("docker"); err != nil { + utils.Log.WithError(err).Fatal("failed to find docker, did you install docker?") + } + return + } + if vagrant { + if _, err := exec.LookPath("vagrant"); err != nil { + utils.Log.WithError(err).Fatal("failed to find vagrant, did you install vagrant?") + } + return + } + + hash := "please install git" + if _, err := exec.LookPath("git"); err == nil { + cmd := exec.Command("git", "rev-parse", "--verify", "HEAD") + cmd.Dir = utils.GoQtPkgPath() + hash = strings.TrimSpace(utils.RunCmdOptional(cmd, "get git hash")) + } + + vars := [][]string{ + {"GOOS", runtime.GOOS}, + {"GOARCH", utils.GOARCH()}, + {"GOVERSION", strings.Split(utils.RunCmd(exec.Command("go", "version"), "get go version"), " ")[2]}, + {"GOROOT", runtime.GOROOT()}, + {"GOPATH", utils.MustGoPath()}, + {"GOBIN", utils.GOBIN()}, + {"QT_HASH", hash}, + {"QT_API", utils.QT_API("")}, + {"QT_VERSION", utils.QT_VERSION()}, + {"QT_VERSION_MAJOR", utils.QT_VERSION_MAJOR()}, + {"QT_DIR", utils.QT_DIR()}, + {"QT_STUB", fmt.Sprint(utils.QT_STUB())}, + {"QT_DEBUG", fmt.Sprint(utils.QT_DEBUG())}, + {"QT_QMAKE_DIR", utils.QT_QMAKE_DIR()}, + {"QT_WEBKIT", fmt.Sprint(utils.QT_WEBKIT())}, + } + + if utils.CI() { + vars = append(vars, [][]string{ + {"CI", fmt.Sprint(utils.CI())}, + }...) + } + + switch target { + case "darwin", "ios", "ios-simulator": + vars = append(vars, [][]string{ + {"QT_HOMEBREW", fmt.Sprint(utils.QT_HOMEBREW())}, + {"QT_MACPORTS", fmt.Sprint(utils.QT_MACPORTS())}, + {"QT_NIX", fmt.Sprint(utils.QT_NIX())}, + {"XCODE_DIR", utils.XCODE_DIR()}, + //{"IPHONEOS_SDK_DIR", utils.IPHONEOS_SDK_DIR()}, //TODO: re-add with absolute path + //{"IPHONESIMULATOR_SDK_DIR", utils.IPHONESIMULATOR_SDK_DIR()}, //TODO: re-add with absolute path + }...) + + if _, err := exec.LookPath("clang++"); err != nil { + utils.Log.WithError(err).Panic("failed to find clang++, did you install Xcode?; please run: xcode-select --install") + } + + case "linux", "ubports": + vars = append(vars, [][]string{ + {"QT_DISTRO", utils.QT_DISTRO()}, + {"QT_PKG_CONFIG", fmt.Sprint(utils.QT_PKG_CONFIG())}, + }...) + + if utils.QT_PKG_CONFIG() { + vars = append(vars, [][]string{ + {"QT_DOC_DIR", utils.QT_DOC_DIR()}, + {"QT_MISC_DIR", utils.QT_MISC_DIR()}, + }...) + } + + if _, err := exec.LookPath("g++"); err != nil { + utils.Log.WithError(err).Panic("failed to find g++, did you install g++?") + } + + case "windows": + if runtime.GOOS == target { + vars = append(vars, [][]string{ + {"QT_MSYS2", fmt.Sprint(utils.QT_MSYS2())}, + }...) + + if utils.QT_MSYS2() { + vars = append(vars, [][]string{ + {"QT_MSYS2_DIR", utils.QT_MSYS2_DIR()}, + {"QT_MSYS2_ARCH", utils.QT_MSYS2_ARCH()}, + {"QT_MSYS2_STATIC", fmt.Sprint(utils.QT_MSYS2_STATIC())}, + }...) + } + + if _, err := exec.LookPath("g++"); err != nil && !utils.QT_MSYS2() { + utils.Log.WithError(err).Panic("failed to find g++, did you start the MinGW shell?") + } + } else { + vars = append(vars, [][]string{ + {"QT_MXE_DIR", utils.QT_MXE_DIR()}, + {"QT_MXE_ARCH", utils.QT_MXE_ARCH()}, + {"QT_MXE_STATIC", fmt.Sprint(utils.QT_MXE_STATIC())}, + }...) + } + + case "android", "android-emulator": + vars = append(vars, [][]string{ + {"JDK_DIR", utils.JDK_DIR()}, + {"ANDROID_SDK_DIR", utils.ANDROID_SDK_DIR()}, + {"ANDROID_NDK_DIR", utils.ANDROID_NDK_DIR()}, + }...) + + case "sailfish", "sailfish-emulator": + vars = append(vars, [][]string{ + {"VIRTUALBOX_DIR", utils.VIRTUALBOX_DIR()}, + {"SAILFISH_DIR", utils.SAILFISH_DIR()}, + }...) + + case "rpi1", "rpi2", "rpi3": + vars = append(vars, [][]string{ + {"RPI_TOOLS_DIR", utils.RPI_TOOLS_DIR()}, + {"RPI_COMPILER", utils.RPI_COMPILER()}, + }...) + } + + for _, v := range vars { + var set string + if _, ok := os.LookupEnv(v[0]); ok { + set = "*" + } + utils.Log.Infof("%v:%v%v%v'%v'", v[0], strings.Repeat(" ", 25-len(v[0])), set, strings.Repeat(" ", 3-len(set)), v[1]) + if !strings.HasSuffix(v[0], "_DIR") { + continue + } + if v[0] == "QT_DIR" && (utils.QT_HOMEBREW() || utils.QT_MACPORTS() || utils.QT_NIX() || utils.QT_MSYS2() || utils.QT_PKG_CONFIG() || utils.MSYS_DOCKER()) { + continue + } + if _, err := ioutil.ReadDir(v[1]); err != nil && v[1] != "" { + utils.Log.WithError(err).Panicf("failed to find %v (%v)", v[0], v[1]) + } + } +} diff --git a/qt/tool-chain/cmd/setup/generate.go b/qt/tool-chain/cmd/setup/generate.go new file mode 100644 index 0000000..f17d694 --- /dev/null +++ b/qt/tool-chain/cmd/setup/generate.go @@ -0,0 +1,69 @@ +package setup + +import ( + "fmt" + "os" + "os/exec" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/binding/templater" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Generate(target string, docker, vagrant bool) { + utils.Log.Infof("running: 'qtsetup generate %v' [docker=%v] [vagrant=%v]", target, docker, vagrant) + if docker { + cmd.Docker([]string{"/home/user/work/bin/qtsetup", "-debug", "generate"}, "linux", "", true) + return + } + + parser.LoadModules() + + mode := "full" + switch { + case target == "js", target == "wasm": + + case target != runtime.GOOS: + mode = "cgo" + + case utils.QT_STUB(): + mode = "stub" + } + + if target == "windows" && runtime.GOOS == target { + os.Setenv("QT_DEBUG_CONSOLE", "true") + } + + for _, module := range parser.GetLibs() { + if !parser.ShouldBuildForTarget(module, target) { + utils.Log.Debugf("skipping generation of %v for %v", module, target) + continue + } + + var license string + switch module { + case "Charts", "DataVisualization": + license = strings.Repeat(" ", 20-len(module)) + "[GPLv3]" + } + utils.Log.Infof("generating %v qt/%v %v", mode, strings.ToLower(module), license) + + if target == runtime.GOOS || utils.QT_FAT() || (mode == "full" && (target == "js" || target == "wasm")) { //TODO: REVIEW + templater.GenModule(module, target, templater.NONE) + } else { + templater.CgoTemplate(module, "", target, templater.MINIMAL, "", "") //TODO: collect errors + } + + if utils.QT_DYNAMIC_SETUP() && mode == "full" && (target != "js" && target != "wasm") { + cc, _ := templater.ParseCgo(strings.ToLower(module), target) + if cc != "" { + cmd := exec.Command("go", "tool", "cgo", utils.GoQtPkgPath(strings.ToLower(module), strings.ToLower(module)+".go")) + cmd.Dir = utils.GoQtPkgPath(strings.ToLower(module)) + utils.RunCmdOptional(cmd, fmt.Sprintf("failed to run cgo for %v (%v) on %v", target, strings.ToLower(module), runtime.GOOS)) + } + } + } +} diff --git a/qt/tool-chain/cmd/setup/install.go b/qt/tool-chain/cmd/setup/install.go new file mode 100644 index 0000000..5d6854a --- /dev/null +++ b/qt/tool-chain/cmd/setup/install.go @@ -0,0 +1,137 @@ +package setup + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/parser" + "github.com/peterq/pan-light/qt/tool-chain/binding/templater" + + "github.com/peterq/pan-light/qt/tool-chain/cmd" + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Install(target string, docker, vagrant bool) { + utils.Log.Infof("running: 'qtsetup install %v' [docker=%v] [vagrant=%v]", target, docker, vagrant) + + if strings.HasPrefix(target, "sailfish") && !utils.QT_SAILFISH() { + if _, err := ioutil.ReadDir(filepath.Join(runtime.GOROOT(), "bin", "linux_386")); err != nil { + build := exec.Command("go", "tool", "dist", "test", "-rebuild", "-run=no_tests") + + env := map[string]string{ + "PATH": os.Getenv("PATH"), + "GOPATH": utils.GOPATH(), + "GOROOT": runtime.GOROOT(), + + "GOOS": "linux", + "GOARCH": "386", + } + + if runtime.GOOS == "windows" { + env["TMP"] = os.Getenv("TMP") + env["TEMP"] = os.Getenv("TEMP") + } + + for k, v := range env { + build.Env = append(build.Env, fmt.Sprintf("%v=%v", k, v)) + } + + utils.RunCmd(build, "setup linux go tools for sailfish") + } + } + + if !(target == runtime.GOOS || target == "js" || target == "wasm") && !utils.QT_FAT() { + utils.Log.Debugf("target is %v; skipping installation of modules", target) + return + } + + env, tags, _, _ := cmd.BuildEnv(target, "", "") + var failed []string + for _, module := range parser.GetLibs() { + if !parser.ShouldBuildForTarget(module, target) { + utils.Log.Debugf("skipping installation of %v for %v", module, target) + continue + } + + mode := "full" + if utils.QT_STUB() || docker { + mode = "stub" + } + + var license string + switch module { + case "Charts", "DataVisualization": + license = strings.Repeat(" ", 20-len(module)) + "[GPLv3]" + } + utils.Log.Infof("installing %v qt/%v %v", mode, strings.ToLower(module), license) + + if utils.QT_DYNAMIC_SETUP() && mode == "full" { + cc, com := templater.ParseCgo(strings.ToLower(module), target) + if cc != "" { + if target == "js" || target == "wasm" { + com = strings.Replace(com, strings.ToLower(module)+".js_plugin_import.cpp ", "", -1) + } + cmd := exec.Command(cc, strings.Split(com, " ")...) + cmd.Dir = utils.GoQtPkgPath(strings.ToLower(module)) + if target == "js" || target == "wasm" { + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + } + utils.RunCmdOptional(cmd, fmt.Sprintf("failed to create dynamic lib for %v (%v) on %v", target, strings.ToLower(module), runtime.GOOS)) + + if target == "js" || target == "wasm" { + continue + } + + utils.RemoveAll(utils.GoQtPkgPath(strings.ToLower(module), strings.ToLower(module)+".cpp")) + utils.RemoveAll(utils.GoQtPkgPath(strings.ToLower(module), "_obj")) + + templater.ReplaceCgo(strings.ToLower(module), target) + } + } + + cmd := exec.Command("go", "install", "-i", "-p", strconv.Itoa(runtime.GOMAXPROCS(0)), "-v") + if len(tags) > 0 { + cmd.Args = append(cmd.Args, fmt.Sprintf("-tags=\"%v\"", strings.Join(tags, "\" \""))) + } + if target != runtime.GOOS { + cmd.Args = append(cmd.Args, []string{"-pkgdir", filepath.Join(utils.MustGoPath(), "pkg", fmt.Sprintf("%v_%v_%v", strings.Replace(target, "-", "_", -1), env["GOOS"], env["GOARCH"]))}...) + } + + if target == "js" { + cmd = exec.Command(filepath.Join(utils.GOBIN(), "gopherjs"), "install") + } + + cmd.Args = append(cmd.Args, fmt.Sprintf("github.com/peterq/pan-light/qt/%v", strings.ToLower(module))) + + if target == "js" { + cmd.Args = append(cmd.Args, "-v") + } else { + if target == "linux" { + delete(env, "CGO_LDFLAGS") + } + for key, value := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", key, value)) + } + } + + if msg, err := utils.RunCmdOptionalError(cmd, fmt.Sprintf("install %v", strings.ToLower(module))); err != nil { + println(msg) + failed = append(failed, strings.ToLower(module)) + } + } + + if l := len(failed); l > 0 { + utils.Log.Warn("failed to install:") + for _, f := range failed { + utils.Log.Warn(f) + } + } +} diff --git a/qt/tool-chain/cmd/setup/prep.go b/qt/tool-chain/cmd/setup/prep.go new file mode 100644 index 0000000..55594a3 --- /dev/null +++ b/qt/tool-chain/cmd/setup/prep.go @@ -0,0 +1,64 @@ +package setup + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Prep() { + utils.Log.Info("running: 'qtsetup prep'") + + errString := "failed to create %v symlink in your PATH (%v); please use %v instead" + sucString := "successfully created %v symlink in your PATH (%v)" + + for _, app := range []string{"qtrcc", "qtmoc", "qtminimal", "qtdeploy", "go"} { + if app == "go" && !(utils.QT_MSYS2() && !utils.QT_DOCKER()) { + continue + } + + if runtime.GOOS == "windows" { + sPath := filepath.Join(utils.GOBIN(), fmt.Sprintf("%v.exe", app)) + dPath := filepath.Join(runtime.GOROOT(), "bin", fmt.Sprintf("%v.exe", app)) + if utils.QT_MSYS2() && !utils.QT_DOCKER() { + if app == "go" { + sPath = dPath + } + dPath = filepath.Join(utils.QT_MSYS2_DIR(), "..", "usr", "bin", fmt.Sprintf("%v.exe", app)) + } + if sPath == dPath { + continue + } + utils.RemoveAll(dPath) + if err := os.Link(sPath, dPath); err == nil { + utils.Log.Infof(sucString, app, dPath) + } else { + utils.Log.Warnf(errString, app, dPath, sPath) + } + continue + } else { + var suc bool + sPath := filepath.Join(utils.GOBIN(), app) + var dPath string + for _, pdPath := range filepath.SplitList("/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:" + filepath.Join(filepath.Join(runtime.GOROOT(), "bin"))) { + dPath = filepath.Join(pdPath, app) + if sPath == dPath { + continue + } + utils.RemoveAll(dPath) + if err := os.Symlink(sPath, dPath); err == nil { + suc = true + break + } + } + if suc { + utils.Log.Infof(sucString, app, dPath) + } else { + utils.Log.Warnf(errString, app, dPath, sPath) + } + } + } +} diff --git a/qt/tool-chain/cmd/setup/test.go b/qt/tool-chain/cmd/setup/test.go new file mode 100644 index 0000000..cc75593 --- /dev/null +++ b/qt/tool-chain/cmd/setup/test.go @@ -0,0 +1,163 @@ +package setup + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/binding/templater" + + "github.com/peterq/pan-light/qt/tool-chain/cmd/deploy" + "github.com/peterq/pan-light/qt/tool-chain/cmd/minimal" + "github.com/peterq/pan-light/qt/tool-chain/cmd/moc" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Test(target string, docker, vagrant bool, vagrantsystem string) { + if docker && target == "darwin" { + utils.Log.Warn("darwin is currently not supported as a deploy target with docker; testing the linux deployment instead") + target = "linux" + } + + utils.Log.Infof("running: 'qtsetup test %v' [docker=%v] [vagrant=%v]", target, docker, vagrant) + + if utils.CI() && target == runtime.GOOS && runtime.GOOS != "windows" { //TODO: test on windows + utils.Log.Infof("running setup/test %v CI", target) + + path := utils.GoQtPkgPath("tool-chain", "cmd", "moc", "test") + + moc.Moc(path, target, "", false, false) + minimal.Minimal(path, target, "") + + var pattern string + if strings.Contains(runtime.Version(), "1.1") || strings.Contains(runtime.Version(), "devel") { + pattern = "all=" + } + + cmd := exec.Command("go", "test", "-v", "-tags=minimal", fmt.Sprintf("-ldflags=%v\"-s\"", pattern)) + cmd.Env = append(os.Environ(), "GODEBUG=cgocheck=2") + cmd.Dir = path + utils.RunCmd(cmd, "run \"go test\"") + } + + mode := "test" + var examples map[string][]string + if utils.CI() { + mode = "build" + examples = map[string][]string{ + "androidextras": []string{"jni", "notification"}, + + "canvas3d": []string{"framebuffer", "interaction", "jsonmodels", + "quickitemtexture", "textureandlight", + filepath.Join("threejs", "cellphone"), + filepath.Join("threejs", "oneqt"), + filepath.Join("threejs", "planets"), + }, + + "charts": []string{"audio"}, + + "common": []string{"qml_demo", "widgets_demo"}, + + //"grpc": []string{"hello_world","hello_world2"}, + + //"gui": []string{"analogclock", "openglwindow", "rasterwindow"}, + + //opengl: []string{"2dpainting"}, + + "qml": []string{"adding", "application", "drawer_nav_x", + filepath.Join("extending", "chapter1-basics"), + filepath.Join("extending", "chapter2-methods"), + filepath.Join("extending", "chapter3-bindings"), + filepath.Join("extending", "chapter4-customPropertyTypes"), + filepath.Join("extending", "components", "test_dir"), + filepath.Join("extending", "components", "test_dir_2"), + filepath.Join("extending", "components", "test_go"), + filepath.Join("extending", "components", "test_module"), + filepath.Join("extending", "components", "test_module_2"), + filepath.Join("extending", "components", "test_qml"), + filepath.Join("extending", "components", "test_qml_go"), + "gallery", "material", + //filepath.Join("printslides", "cmd", "printslides"), + "prop", "prop2" /*"quickflux", "webview"*/}, + + "qt3d": []string{"audio-visualizer-qml"}, + + "quick": []string{"bridge", "bridge2", "calc", "dialog", "dynamic", + "hotreload", "listview", "sailfish", "tableview", "translate", "view"}, + + "sailfish": []string{"listview", "listview_variant"}, + + "showcases": []string{"sia"}, + + "sql": []string{"masterdetail", "masterdetail_qml", "querymodel"}, + + "uitools": []string{"calculator"}, + + "webchannel": []string{"chatserver-go" /*"standalone" "webview"*/}, + + "widgets": []string{"bridge2" /*"dropsite"*/, "graphicsscene", "line_edits", "pixel_editor", + /*"renderer"*/ "share", "systray" /*"table"*/, "textedit", filepath.Join("treeview", "treeview_dual"), + filepath.Join("treeview", "treeview_filelist"), "video_player" /*"webengine"*/, "xkcd"}, + } + } else { + if strings.HasPrefix(target, "sailfish") { + examples = map[string][]string{ + "quick": []string{"sailfish"}, + + "sailfish": []string{"listview", "listview_variant"}, + } + } else { + examples = map[string][]string{ + "qml": []string{"application", "drawer_nav_x", "gallery"}, + + "quick": []string{"calc"}, + + "widgets": []string{"line_edits", "pixel_editor", "textedit"}, + } + } + } + + if utils.QT_VAGRANT() && target == "ios-simulator" { + mode = "build" + } + + for cat, list := range examples { + for _, example := range list { + if target != runtime.GOOS && example == "textedit" { + continue + } + + if (target == "js" || target == "wasm") && + cat == "charts" || cat == "uitools" || cat == "sql" || + cat == "androidextras" || cat == "qt3d" || cat == "webchannel" || + (cat == "widgets" && strings.HasPrefix(example, "treeview")) || + example == "video_player" { + continue + } + + example := filepath.Join(cat, example) + + path := filepath.Join(strings.TrimSpace(utils.RunCmdOptional(utils.GoList("{{.Dir}}", "github.com/peterq/pan-light/qt/tool-chain/examples"), "get doc dir")), example) + utils.Log.Infof("testing %v", example) + deploy.Deploy( + mode, + target, + path, + docker, + "", + "", + false, + "", + vagrant, + vagrantsystem, + false, + ) + templater.CleanupDepsForCI() + templater.CleanupDepsForCI = func() {} + } + } +} diff --git a/qt/tool-chain/cmd/setup/update.go b/qt/tool-chain/cmd/setup/update.go new file mode 100644 index 0000000..2ac4b48 --- /dev/null +++ b/qt/tool-chain/cmd/setup/update.go @@ -0,0 +1,58 @@ +package setup + +import ( + "fmt" + "os/exec" + "path/filepath" + "strings" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +func Update() { + utils.Log.Info("running: 'qtsetup update'") + + utils.RunCmd(exec.Command("go", "clean", "-i", "github.com/peterq/pan-light/qt/cmd/..."), "run \"go clean cmd\"") + utils.RunCmd(exec.Command("go", "clean", "-i", "github.com/peterq/pan-light/qt/tool-chain/..."), "run \"go clean tool-chain\"") + + fetch := exec.Command("git", "fetch", "-f", "--all") + fetch.Dir = filepath.Join(utils.MustGoPath(), "src", "github.com", "therecipe", "qt") + utils.RunCmd(fetch, "run \"git fetch\"") + + checkoutCmd := exec.Command("git", "checkout", "-f", "--", utils.GoQtPkgPath("cmd")) + checkoutCmd.Dir = filepath.Join(utils.MustGoPath(), "src", "github.com", "therecipe", "qt") + utils.RunCmd(checkoutCmd, "run \"git checkout cmd\"") + + checkoutInternal := exec.Command("git", "checkout", "-f", "--", utils.GoQtPkgPath("tool-chain")) + checkoutInternal.Dir = filepath.Join(utils.MustGoPath(), "src", "github.com", "therecipe", "qt") + utils.RunCmd(checkoutInternal, "run \"git checkout tool-chain\"") + + hash := "please install git" + if _, err := exec.LookPath("git"); err == nil { + cmd := exec.Command("git", "rev-parse", "--verify", "HEAD") + cmd.Dir = utils.GoQtPkgPath() + hash = strings.TrimSpace(utils.RunCmdOptional(cmd, "get git hash")) + } + + utils.RunCmd(exec.Command("go", "install", "-v", fmt.Sprintf("-ldflags=\"-X=github.com/peterq/pan-light/qt/tool-chain/cmd.buildVersion=%v\"", hash), fmt.Sprintf("github.com/peterq/pan-light/qt/cmd/...")), "run \"go install\"") + + Prep() +} + +func Upgrade() { + utils.Log.Info("running: 'qtsetup upgrade'") + + utils.RunCmd(exec.Command("go", "clean", "-i", "github.com/peterq/pan-light/qt/..."), "run \"go clean\"") + utils.RemoveAll(utils.GoQtPkgPath()) + + utils.RunCmd(exec.Command("go", "get", "-v", "-d", "github.com/peterq/pan-light/qt/cmd/..."), "run \"go get\"") + + hash := "please install git" + if _, err := exec.LookPath("git"); err == nil { + cmd := exec.Command("git", "rev-parse", "--verify", "HEAD") + cmd.Dir = utils.GoQtPkgPath() + hash = strings.TrimSpace(utils.RunCmdOptional(cmd, "get git hash")) + } + + utils.RunCmd(exec.Command("go", "install", "-v", fmt.Sprintf("-ldflags=\"-X=github.com/peterq/pan-light/qt/tool-chain/cmd.buildVersion=%v\"", hash), fmt.Sprintf("github.com/peterq/pan-light/qt/cmd/...")), "run \"go install\"") +} diff --git a/qt/tool-chain/cmd/tools.go b/qt/tool-chain/cmd/tools.go new file mode 100644 index 0000000..d180d7c --- /dev/null +++ b/qt/tool-chain/cmd/tools.go @@ -0,0 +1,7 @@ +package cmd + +import ( + //only needed for module support -> + _ "github.com/gopherjs/gopherjs/js" + //<- +) diff --git a/qt/tool-chain/cmd/utils.go b/qt/tool-chain/cmd/utils.go new file mode 100644 index 0000000..df2e9d3 --- /dev/null +++ b/qt/tool-chain/cmd/utils.go @@ -0,0 +1,165 @@ +package cmd + +import ( + "fmt" + "os/exec" + "path/filepath" + "strings" + "sync" + + "github.com/peterq/pan-light/qt/tool-chain/utils" +) + +var ( + std []string + stdMutex = new(sync.Mutex) + + imported = make(map[string]string) + importedMutex = new(sync.Mutex) +) + +func IsStdPkg(pkg string) bool { + + stdMutex.Lock() + if std == nil { + std = append(strings.Split(strings.TrimSpace(utils.RunCmd(exec.Command("go", "list", "std"), "go list std")), "\n"), "C") + } + stdMutex.Unlock() + + for _, spkg := range std { + if pkg == spkg { + return true + } + } + return false +} + +func GetImports(path, target, tagsCustom string, level int, onlyDirect, moc bool) []string { + utils.Log.WithField("path", path).WithField("level", level).Debug("get imports") + + env, tags, _, _ := BuildEnv(target, "", "") + + stdMutex.Lock() + if std == nil { + std = append(strings.Split(strings.TrimSpace(utils.RunCmd(exec.Command("go", "list", "std"), "go list std")), "\n"), "C") + } + stdMutex.Unlock() + + if tagsCustom != "" { + tags = append(tags, strings.Split(tagsCustom, " ")...) + } + + importMap := make(map[string]struct{}) + + imp := "Deps" + if onlyDirect { + imp = "Imports" + } + + //TODO: cache + cmd := utils.GoList("'{{ join .TestImports \"|\" }}':'{{ join .XTestImports \"|\" }}':'{{ join ."+imp+" \"|\" }}'", fmt.Sprintf("-tags=\"%v\"", strings.Join(tags, "\" \""))) + cmd.Dir = path + for k, v := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", k, v)) + } + + out := strings.TrimSpace(utils.RunCmd(cmd, "go list deps")) + out = strings.Replace(out, "'", "", -1) + out = strings.Replace(out, ":", "|", -1) + libs := strings.Split(out, "|") + + for i := len(libs) - 1; i >= 0; i-- { + if strings.TrimSpace(libs[i]) == "" { + libs = append(libs[:i], libs[i+1:]...) + continue + } + + importedMutex.Lock() + dep, ok := imported[libs[i]] + importedMutex.Unlock() + if ok { + importMap[dep] = struct{}{} + libs = append(libs[:i], libs[i+1:]...) + continue + } + + for _, k := range std { + if libs[i] == k { + libs = append(libs[:i], libs[i+1:]...) + break + } + } + } + + wg := new(sync.WaitGroup) + wc := make(chan bool, 50) + wg.Add(len(libs)) + for _, l := range libs { + wc <- true + go func(l string) { + defer func() { + <-wc + wg.Done() + }() + + if strings.Contains(l, "github.com/peterq/pan-light/qt") && !strings.Contains(l, "qt/tool-chain") { + return + } + + cmd := utils.GoList("{{.Dir}}", fmt.Sprintf("-tags=\"%v\"", strings.Join(tags, "\" \"")), l) + for k, v := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", k, v)) + } + + dep := strings.TrimSpace(utils.RunCmd(cmd, "go list dir")) + if dep == "" { + return + } + + importedMutex.Lock() + importMap[dep] = struct{}{} + imported[l] = dep + importedMutex.Unlock() + }(l) + } + wg.Wait() + + var imports []string + for k := range importMap { + imports = append(imports, k) + } + return imports +} + +func GetGoFiles(path, target, tagsCustom string) []string { + utils.Log.WithField("path", path).WithField("target", target).WithField("tagsCustom", tagsCustom).Debug("get go files") + + env, tags, _, _ := BuildEnv(target, "", "") + if tagsCustom != "" { + tags = append(tags, strings.Split(tagsCustom, " ")...) + } + + //TODO: cache + cmd := utils.GoList("'{{ join .GoFiles \"|\" }}':'{{ join .CgoFiles \"|\" }}':'{{ join .TestGoFiles \"|\" }}':'{{ join .XTestGoFiles \"|\" }}'", fmt.Sprintf("-tags=\"%v\"", strings.Join(tags, "\" \""))) + cmd.Dir = path + for k, v := range env { + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", k, v)) + } + + out := strings.TrimSpace(utils.RunCmd(cmd, "go list gofiles")) + out = strings.Replace(out, "'", "", -1) + out = strings.Replace(out, ":", "|", -1) + + importMap := make(map[string]struct{}) + for _, v := range strings.Split(out, "|") { + if strings.TrimSpace(v) != "" { + importMap[v] = struct{}{} + } + } + + olibs := make([]string, 0) + for k := range importMap { + olibs = append(olibs, filepath.Join(path, k)) + } + return olibs +} diff --git a/qt/tool-chain/docker/android/Dockerfile b/qt/tool-chain/docker/android/Dockerfile new file mode 100644 index 0000000..2204f2b --- /dev/null +++ b/qt/tool-chain/docker/android/Dockerfile @@ -0,0 +1,91 @@ +FROM ubuntu:16.04 as base + +ENV USER user +ENV HOME /home/$USER + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl make openjdk-8-jdk perl unzip +RUN mkdir -p $HOME + +RUN SDK=sdk-tools-linux-3859397.zip && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/android/repository/$SDK && unzip -qq $SDK -d $HOME/android-sdk-linux +RUN $HOME/android-sdk-linux/tools/bin/sdkmanager --list --verbose +RUN echo "y" | $HOME/android-sdk-linux/tools/bin/sdkmanager "platform-tools" "build-tools;28.0.3" "platforms;android-28" +RUN $HOME/android-sdk-linux/tools/bin/sdkmanager --update + +RUN NDK=android-ndk-r15c-linux-x86_64.zip && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/android/repository/$NDK && unzip -qq $NDK -d $HOME + +RUN OSSL=openssl-1.0.2q.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://www.openssl.org/source/$OSSL && tar -xzf $OSSL -C $HOME + +ENV CC $HOME/android-ndk-r15c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc +ENV AR $HOME/android-ndk-r15c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar +ENV ANDROID_DEV $HOME/android-ndk-r15c/platforms/android-16/arch-arm/usr +RUN sed -i 's/LIBCOMPATVERSIONS=/#LIBCOMPATVERSIONS=/' $HOME/openssl-1.0.2q/Makefile +RUN sed -i 's/LIBVERSION=/\\ #LIBVERSION=/' $HOME/openssl-1.0.2q/Makefile +RUN cd $HOME/openssl-1.0.2q && ./Configure shared android-armv7 && make build_libs $HOME/openssl-1.0.2q +RUN mkdir -p $HOME/openssl-1.0.2q-arm && mv $HOME/openssl-1.0.2q/*.so* $HOME/openssl-1.0.2q-arm + +RUN rm -rf $HOME/openssl-1.0.2q && tar -xzf openssl-1.0.2q.tar.gz -C $HOME + +ENV CC $HOME/android-ndk-r15c/toolchains/x86-4.9/prebuilt/linux-x86_64/bin/i686-linux-android-gcc +ENV AR $HOME/android-ndk-r15c/toolchains/x86-4.9/prebuilt/linux-x86_64/bin/i686-linux-android-ar +ENV ANDROID_DEV $HOME/android-ndk-r15c/platforms/android-16/arch-x86/usr +RUN sed -i 's/LIBCOMPATVERSIONS=/#LIBCOMPATVERSIONS=/' $HOME/openssl-1.0.2q/Makefile +RUN sed -i 's/LIBVERSION=/\\ #LIBVERSION=/' $HOME/openssl-1.0.2q/Makefile +RUN cd $HOME/openssl-1.0.2q && ./Configure shared android-x86 && make build_libs $HOME/openssl-1.0.2q +RUN mkdir -p $HOME/openssl-1.0.2q-x86 && mv $HOME/openssl-1.0.2q/*.so* $HOME/openssl-1.0.2q-x86 + + +# +#Clang OpenSSL support is disabled until https://bugreports.qt.io/browse/QTBUG-71391 got resolved +# + +RUN NDK=android-ndk-r18b-linux-x86_64.zip && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/android/repository/$NDK && unzip -qq $NDK -d $HOME + +#RUN OSSL=openssl-1.1.1a.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://www.openssl.org/source/$OSSL && tar -xzf $OSSL -C $HOME + +#ENV ANDROID_NDK $HOME/android-ndk-r18b +#ENV PATH=$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH + +#RUN cd $HOME/openssl-1.1.1a && ./Configure -D__ANDROID_API__=16 android-arm && make SHLIB_VERSION_NUMBER= SHLIB_EXT=.so build_libs $HOME/openssl-1.1.1a +#RUN mkdir -p $HOME/openssl-1.1.1a-arm && mv $HOME/openssl-1.1.1a/*.so* $HOME/openssl-1.1.1a-arm +#RUN rm -rf $HOME/openssl-1.1.1a && tar -xzf openssl-1.1.1a.tar.gz -C $HOME +#RUN cd $HOME/openssl-1.1.1a && ./Configure -D__ANDROID_API__=16 android-x86 && make SHLIB_VERSION_NUMBER= SHLIB_EXT=.so build_libs $HOME/openssl-1.1.1a +#RUN mkdir -p $HOME/openssl-1.1.1a-x86 && mv $HOME/openssl-1.1.1a/*.so* $HOME/openssl-1.1.1a-x86 +#RUN find $HOME/openssl-1.1.1a-* -type f -exec llvm-strip --strip-all {} \; + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV JDK_DIR /usr/lib/jvm/java-8-openjdk-amd64 +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/android_armv7 /opt/Qt/5.12.0/android_armv7 +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/android_x86 /opt/Qt/5.12.0/android_x86 +COPY --from=therecipe/qt:linux /opt/Qt/Docs /opt/Qt/Docs +COPY --from=base $HOME/android-sdk-linux $HOME/android-sdk-linux +COPY --from=base $HOME/android-ndk-r18b $HOME/android-ndk-r18b +COPY --from=base $HOME/openssl-1.0.2q-arm $HOME/openssl-1.0.2q-arm +COPY --from=base $HOME/openssl-1.0.2q-x86 $HOME/openssl-1.0.2q-x86 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates openjdk-8-jdk && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep + +RUN $GOPATH/bin/qtsetup check android +RUN $GOPATH/bin/qtsetup generate android +RUN $GOPATH/bin/qtsetup install android +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/androidextras/jni && $GOPATH/bin/qtdeploy build android || true && rm -rf ./deploy + +RUN $GOPATH/bin/qtsetup check android-emulator +RUN $GOPATH/bin/qtsetup generate android-emulator +RUN $GOPATH/bin/qtsetup install android-emulator +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/androidextras/jni && $GOPATH/bin/qtdeploy build android-emulator || true && rm -rf ./deploy diff --git a/qt/tool-chain/docker/docker_job_template.yml b/qt/tool-chain/docker/docker_job_template.yml new file mode 100644 index 0000000..9e28120 --- /dev/null +++ b/qt/tool-chain/docker/docker_job_template.yml @@ -0,0 +1,9 @@ +jobs: +- job: '${{ parameters.tag }}' + timeoutInMinutes: 0 + pool: + vmImage: ubuntu-16.04 + dependsOn: '${{ parameters.dep }}' + steps: + - script: 'docker build -f tool-chain/docker/${{ parameters.file }} -t therecipe/qt:${{ parameters.tag }} .' + - script: 'docker login -u therecipe -p $(DOCKER_PASSWORD) && docker push therecipe/qt:${{ parameters.tag }} && docker logout' diff --git a/qt/tool-chain/docker/docker_pipelines_template.yml b/qt/tool-chain/docker/docker_pipelines_template.yml new file mode 100644 index 0000000..c66efec --- /dev/null +++ b/qt/tool-chain/docker/docker_pipelines_template.yml @@ -0,0 +1,193 @@ +jobs: +- template: docker_job_template.yml + parameters: + file: android/Dockerfile + tag: android + dep: linux +- template: docker_job_template.yml + parameters: + file: js/Dockerfile + tag: js + dep: + # - js_base + - linux +# - +# template: docker_job_template.yml +# parameters: +# file: js/Dockerfile.base +# tag: js_base +- template: docker_job_template.yml + parameters: + file: js/Dockerfile.wasm + tag: wasm + dep: + # - js_base + - linux +- template: docker_job_template.yml + parameters: + file: linux/Dockerfile + tag: linux +- template: docker_job_template.yml + parameters: + file: linux/Dockerfile.56 + tag: linux_56 +- template: docker_job_template.yml + parameters: + file: linux/Dockerfile.59 + tag: linux_59 +- template: docker_job_template.yml + parameters: + file: linux/Dockerfile.arch + tag: linux_arch +- template: docker_job_template.yml + parameters: + file: linux/Dockerfile.ubuntu_16_04 + tag: linux_ubuntu_16_04 +- template: docker_job_template.yml + parameters: + file: linux/Dockerfile.ubuntu_18_04 + tag: linux_ubuntu_18_04 +- template: docker_job_template.yml + parameters: + file: sailfish/Dockerfile + tag: sailfish +- template: docker_job_template.yml + parameters: + file: ubports/Dockerfile.64_vivid + tag: ubports_64_vivid + dep: linux +- template: docker_job_template.yml + parameters: + file: ubports/Dockerfile.64_xenial + tag: ubports_64_xenial + dep: linux +- template: docker_job_template.yml + parameters: + file: ubports/Dockerfile.arm_vivid + tag: ubports_arm_vivid + dep: linux +- template: docker_job_template.yml + parameters: + file: ubports/Dockerfile.arm_xenial + tag: ubports_arm_xenial + dep: linux +- template: docker_job_template.yml + parameters: + file: windows_32_shared/Dockerfile + tag: windows_32_shared + dep: + # - windows_32_shared_base + - linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_32_shared/Dockerfile.base +# tag: windows_32_shared_base +- template: docker_job_template.yml + parameters: + file: windows_32_static/Dockerfile + tag: windows_32_static + dep: + # - windows_32_static_base + - linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_32_static/Dockerfile.base +# tag: windows_32_static_base +- template: docker_job_template.yml + parameters: + file: windows_64_shared/Dockerfile + tag: windows_64_shared + dep: + # - windows_64_shared_base + - linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_64_shared/Dockerfile.base +# tag: windows_64_shared_base +- template: docker_job_template.yml + parameters: + file: windows_64_static/Dockerfile + tag: windows_64_static + dep: + # - windows_64_static_base + - linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_64_static/Dockerfile.base +# tag: windows_64_static_base +# - +# template: docker_job_template.yml +# parameters: +# file: windows_legacy/Dockerfile.32_shared +# tag: windows_32_shared_legacy +# dep: linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_legacy/Dockerfile.32_static +# tag: windows_32_static_legacy +# dep: linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_legacy/Dockerfile.64_shared +# tag: windows_64_shared_legacy +# dep: linux +# - +# template: docker_job_template.yml +# parameters: +# file: windows_legacy/Dockerfile.64_static +# tag: windows_64_static_legacy +# dep: linux +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile + tag: windows_64_shared_wine + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.32_shared + tag: windows_32_shared_msys2 + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.32_static + tag: windows_32_static_msys2 + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.56 + tag: windows_32_shared_56 + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.56_xp + tag: windows_32_shared_xp + dep: wine_base_xp +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.59 + tag: windows_32_shared_59 + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.64_shared + tag: windows_64_shared_msys2 + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.64_static + tag: windows_64_static_msys2 + dep: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.base + tag: wine_base +- template: docker_job_template.yml + parameters: + file: wine/Dockerfile.base_xp + tag: wine_base_xp diff --git a/qt/tool-chain/docker/js/Dockerfile b/qt/tool-chain/docker/js/Dockerfile new file mode 100644 index 0000000..f7a8e08 --- /dev/null +++ b/qt/tool-chain/docker/js/Dockerfile @@ -0,0 +1,32 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_QMAKE_DIR /usr/local/Qt-5.12.0/bin + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:linux /opt/Qt/Docs /opt/Qt/Docs +COPY --from=therecipe/qt:js_base $HOME/emsdk $HOME/emsdk +COPY --from=therecipe/qt:js_base $HOME/.emscripten $HOME/.emscripten +COPY --from=therecipe/qt:js_base /usr/local/Qt-5.12.0 /usr/local/Qt-5.12.0 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install git && apt-get -qq clean +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install python2.7 nodejs cmake default-jre && apt-get -qq clean +RUN ln -s /usr/bin/python2.7 /usr/bin/python + +RUN go get github.com/gopherjs/gopherjs + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check js +RUN QT_STUB=true $GOPATH/bin/qtsetup generate +RUN $GOPATH/bin/qtsetup generate js +RUN $GOPATH/bin/qtsetup install js +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build js && rm -rf ./deploy diff --git a/qt/tool-chain/docker/js/Dockerfile.base b/qt/tool-chain/docker/js/Dockerfile.base new file mode 100644 index 0000000..bf35f06 --- /dev/null +++ b/qt/tool-chain/docker/js/Dockerfile.base @@ -0,0 +1,17 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install build-essential git + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install python2.7 nodejs cmake default-jre +RUN ln -s /usr/bin/python2.7 /usr/bin/python +RUN git clone -q --depth 1 https://github.com/juj/emsdk.git $HOME/emsdk && cd $HOME/emsdk && ./emsdk install latest && ./emsdk activate latest + +RUN git clone -q --depth 1 -b 5.12.0 --recursive https://code.qt.io/qt/qt5.git /opt/qt5 + +RUN echo "#!/bin/bash\nsource $HOME/emsdk/emsdk_env.sh \ + && cd /opt/qt5 && ./configure -xplatform wasm-emscripten -optimize-size -nomake tests -nomake examples -skip qtpim -skip qtfeedback -skip qtwinextras -skip qttools -confirm-license -opensource && make && make install" > $HOME/build.sh \ + && chmod +x $HOME/build.sh && $HOME/build.sh diff --git a/qt/tool-chain/docker/js/Dockerfile.wasm b/qt/tool-chain/docker/js/Dockerfile.wasm new file mode 100644 index 0000000..12be2c2 --- /dev/null +++ b/qt/tool-chain/docker/js/Dockerfile.wasm @@ -0,0 +1,38 @@ +FROM therecipe/qt:linux as base + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates git + +RUN mv /usr/local/go /usr/local/go_orig +RUN git clone -q --depth 1 -b wasm-sync-callbacks https://github.com/neelance/go.git /usr/local/go +RUN cd /usr/local/go/src && GOROOT_BOOTSTRAP=/usr/local/go_orig ./make.bash + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_QMAKE_DIR /usr/local/Qt-5.12.0/bin + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:linux /opt/Qt/Docs /opt/Qt/Docs +COPY --from=therecipe/qt:js_base $HOME/emsdk $HOME/emsdk +COPY --from=therecipe/qt:js_base $HOME/.emscripten $HOME/.emscripten +COPY --from=therecipe/qt:js_base /usr/local/Qt-5.12.0 /usr/local/Qt-5.12.0 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install python2.7 nodejs cmake default-jre && apt-get -qq clean +RUN ln -s /usr/bin/python2.7 /usr/bin/python + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check wasm +RUN QT_STUB=true $GOPATH/bin/qtsetup generate +RUN $GOPATH/bin/qtsetup generate wasm +RUN $GOPATH/bin/qtsetup install wasm +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build wasm && rm -rf ./deploy diff --git a/qt/tool-chain/docker/linux/Dockerfile b/qt/tool-chain/docker/linux/Dockerfile new file mode 100644 index 0000000..658b30f --- /dev/null +++ b/qt/tool-chain/docker/linux/Dockerfile @@ -0,0 +1,42 @@ +FROM ubuntu:16.04 as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install dbus fontconfig libx11-6 libx11-xcb1 +RUN QT=qt-unified-linux-x64-online.run && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/online_installers/$QT && chmod +x $QT && QT_QPA_PLATFORM=minimal ./$QT --script $GOPATH/src/github.com/peterq/pan-light/qt/internal/ci/iscript.qs LINUX=true +RUN find /opt/Qt/5.12.0 -type f -name "*.debug" -delete +RUN find /opt/Qt/Docs -type f ! -name "*.index" -delete +RUN find /opt/Qt/5.12.0/gcc_64 -type f ! -name "*.a" -name "lib*" -exec strip -s {} \; + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true + + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=base /opt/Qt/5.12.0 /opt/Qt/5.12.0 +COPY --from=base /opt/Qt/Docs /opt/Qt/Docs +COPY --from=base /opt/Qt/Licenses /opt/Qt/Licenses + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install build-essential libglib2.0-dev libglu1-mesa-dev libpulse-dev \ + && apt-get --no-install-recommends -qq -y install fontconfig libasound2 libegl1-mesa libnss3 libpci3 libxcomposite1 libxcursor1 libxi6 libxrandr2 libxtst6 && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check +RUN $GOPATH/bin/qtsetup generate +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build linux && rm -rf ./deploy diff --git a/qt/tool-chain/docker/linux/Dockerfile.56 b/qt/tool-chain/docker/linux/Dockerfile.56 new file mode 100644 index 0000000..4665ffc --- /dev/null +++ b/qt/tool-chain/docker/linux/Dockerfile.56 @@ -0,0 +1,40 @@ +FROM ubuntu:16.04 as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install dbus fontconfig libx11-6 libx11-xcb1 +RUN QT=qt-unified-linux-x64-online.run && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/online_installers/$QT && chmod +x $QT && QT_QPA_PLATFORM=minimal ./$QT --script $GOPATH/src/github.com/peterq/pan-light/qt/internal/ci/iscript.qs LINUX=true VERSION=563 +RUN find /opt/Qt/5.6.3 -type f -name "*.debug" -delete +RUN find /opt/Qt/Docs -type f ! -name "*.index" -delete + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_VERSION 5.6.3 + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=base /opt/Qt/5.6.3/gcc_64 /opt/Qt/5.6.3/gcc_64 +COPY --from=base /opt/Qt/Docs /opt/Qt/Docs + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install build-essential libglib2.0-dev libglu1-mesa-dev libpulse-dev \ + && apt-get --no-install-recommends -qq -y install fontconfig libasound2 libegl1-mesa libnss3 libpci3 libxcomposite1 libxcursor1 libxi6 libxrandr2 libxtst6 && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check +RUN $GOPATH/bin/qtsetup generate +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build linux && rm -rf ./deploy diff --git a/qt/tool-chain/docker/linux/Dockerfile.59 b/qt/tool-chain/docker/linux/Dockerfile.59 new file mode 100644 index 0000000..a84bcb2 --- /dev/null +++ b/qt/tool-chain/docker/linux/Dockerfile.59 @@ -0,0 +1,40 @@ +FROM ubuntu:16.04 as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install dbus fontconfig libx11-6 libx11-xcb1 +RUN QT=qt-unified-linux-x64-online.run && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/online_installers/$QT && chmod +x $QT && QT_QPA_PLATFORM=minimal ./$QT --script $GOPATH/src/github.com/peterq/pan-light/qt/internal/ci/iscript.qs LINUX=true VERSION=596 +RUN find /opt/Qt/5.9.6 -type f -name "*.debug" -delete +RUN find /opt/Qt/Docs -type f ! -name "*.index" -delete + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_VERSION 5.9.6 + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=base /opt/Qt/5.9.6/gcc_64 /opt/Qt/5.9.6/gcc_64 +COPY --from=base /opt/Qt/Docs /opt/Qt/Docs + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install build-essential libglib2.0-dev libglu1-mesa-dev libpulse-dev \ + && apt-get --no-install-recommends -qq -y install fontconfig libasound2 libegl1-mesa libnss3 libpci3 libxcomposite1 libxcursor1 libxi6 libxrandr2 libxtst6 && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check +RUN $GOPATH/bin/qtsetup generate +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build linux && rm -rf ./deploy diff --git a/qt/tool-chain/docker/linux/Dockerfile.arch b/qt/tool-chain/docker/linux/Dockerfile.arch new file mode 100644 index 0000000..d65179a --- /dev/null +++ b/qt/tool-chain/docker/linux/Dockerfile.arch @@ -0,0 +1,32 @@ +FROM base/archlinux as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN pacman -Syyu --quiet && pacman -S --noconfirm --needed --noprogressbar --quiet ca-certificates curl git +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +FROM base/archlinux +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_API 5.12.0 +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN pacman -Syyu --quiet && pacman -S --noconfirm --needed --noprogressbar --quiet base-devel pkg-config && pacman -Scc --noconfirm --noprogressbar --quiet +RUN pacman -Syyu --quiet && pacman -S --noconfirm --needed --noprogressbar --quiet qt5 && pacman -Scc --noconfirm --noprogressbar --quiet + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check +RUN $GOPATH/bin/qtsetup generate +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build linux && rm -rf ./deploy \ No newline at end of file diff --git a/qt/tool-chain/docker/linux/Dockerfile.ubuntu_16_04 b/qt/tool-chain/docker/linux/Dockerfile.ubuntu_16_04 new file mode 100644 index 0000000..6a7c39d --- /dev/null +++ b/qt/tool-chain/docker/linux/Dockerfile.ubuntu_16_04 @@ -0,0 +1,31 @@ +FROM ubuntu:16.04 as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install build-essential libglib2.0-dev libglu1-mesa-dev libpulse-dev && apt-get -qq clean +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install libqt*5-dev qt*5-dev qt*5-doc-html && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check +RUN $GOPATH/bin/qtsetup generate +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/bridge2 && $GOPATH/bin/qtdeploy build linux && rm -rf ./deploy diff --git a/qt/tool-chain/docker/linux/Dockerfile.ubuntu_18_04 b/qt/tool-chain/docker/linux/Dockerfile.ubuntu_18_04 new file mode 100644 index 0000000..823b5f9 --- /dev/null +++ b/qt/tool-chain/docker/linux/Dockerfile.ubuntu_18_04 @@ -0,0 +1,31 @@ +FROM ubuntu:18.04 as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +FROM ubuntu:18.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install build-essential libglib2.0-dev libglu1-mesa-dev libpulse-dev && apt-get -qq clean +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install libqt*5-dev qt*5-dev qt*5-doc-html && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check +RUN $GOPATH/bin/qtsetup generate +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build linux && rm -rf ./deploy diff --git a/qt/tool-chain/docker/rpi/Dockerfile.base b/qt/tool-chain/docker/rpi/Dockerfile.base new file mode 100644 index 0000000..fbb8f1b --- /dev/null +++ b/qt/tool-chain/docker/rpi/Dockerfile.base @@ -0,0 +1,27 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_RPI true +ENV QT_QMAKE_DIR /opt/qtrpi/raspi/qt5/bin +ENV RPI_TOOLS_DIR /opt/qtrpi/raspi/tools +ENV RPI_COMPILER gcc-linaro-arm-linux-gnueabihf-raspbian-x64 + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git make unzip && apt-get -qq clean + +RUN SYSROOT=qtrpi-sysroot-minimal-latest.zip && curl -sL --retry 10 --retry-delay 60 -O http://www.qtrpi.com/downloads/sysroot/$SYSROOT && unzip -qq $SYSROOT -d /opt/qtrpi && rm -f $SYSROOT +RUN ln -s /opt/qtrpi/raspbian/sysroot-minimal /opt/qtrpi/raspbian/sysroot + +RUN git clone -q --depth 1 https://github.com/raspberrypi/tools.git /opt/qtrpi/raspi/tools + +RUN $GOPATH/bin/qtsetup prep diff --git a/qt/tool-chain/docker/rpi/Dockerfile.rpi1 b/qt/tool-chain/docker/rpi/Dockerfile.rpi1 new file mode 100644 index 0000000..4b82da5 --- /dev/null +++ b/qt/tool-chain/docker/rpi/Dockerfile.rpi1 @@ -0,0 +1,10 @@ +FROM therecipe/qt:rpi_base +LABEL maintainer therecipe + +RUN QT=qtrpi-rpi1_qt-5.7.0.zip && curl -sL --retry 10 --retry-delay 60 -O http://www.qtrpi.com/downloads/qtrpi/rpi1/$QT && unzip -qq $QT -d /opt/qtrpi && rm -f $QT +RUN git clone -q --depth 1 -b 5.7 https://code.qt.io/qt/qtvirtualkeyboard.git /opt/qtrpi/raspi/qtvirtualkeyboard && cd /opt/qtrpi/raspi/qtvirtualkeyboard && ../qt5/bin/qmake qtvirtualkeyboard.pro CONFIG+=lang-all && make && make install && rm -rf /opt/qtrpi/raspi/qtvirtualkeyboard + +RUN $GOPATH/bin/qtsetup check rpi1 +RUN $GOPATH/bin/qtsetup generate rpi1 +RUN $GOPATH/bin/qtsetup install rpi1 +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build rpi1 && rm -rf ./deploy diff --git a/qt/tool-chain/docker/rpi/Dockerfile.rpi2 b/qt/tool-chain/docker/rpi/Dockerfile.rpi2 new file mode 100644 index 0000000..6a21cef --- /dev/null +++ b/qt/tool-chain/docker/rpi/Dockerfile.rpi2 @@ -0,0 +1,10 @@ +FROM therecipe/qt:rpi_base +LABEL maintainer therecipe + +RUN QT=qtrpi-rpi2_qt-5.7.0.zip && curl -sL --retry 10 --retry-delay 60 -O http://www.qtrpi.com/downloads/qtrpi/rpi2/$QT && unzip -qq $QT -d /opt/qtrpi && rm -f $QT +RUN git clone -q --depth 1 -b 5.7 https://code.qt.io/qt/qtvirtualkeyboard.git /opt/qtrpi/raspi/qtvirtualkeyboard && cd /opt/qtrpi/raspi/qtvirtualkeyboard && ../qt5/bin/qmake qtvirtualkeyboard.pro CONFIG+=lang-all && make && make install && rm -rf /opt/qtrpi/raspi/qtvirtualkeyboard + +RUN $GOPATH/bin/qtsetup check rpi2 +RUN $GOPATH/bin/qtsetup generate rpi2 +RUN $GOPATH/bin/qtsetup install rpi2 +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build rpi2 && rm -rf ./deploy diff --git a/qt/tool-chain/docker/rpi/Dockerfile.rpi3 b/qt/tool-chain/docker/rpi/Dockerfile.rpi3 new file mode 100644 index 0000000..5504663 --- /dev/null +++ b/qt/tool-chain/docker/rpi/Dockerfile.rpi3 @@ -0,0 +1,10 @@ +FROM therecipe/qt:rpi_base +LABEL maintainer therecipe + +RUN QT=qtrpi-rpi3_qt-5.7.0.zip && curl -sL --retry 10 --retry-delay 60 -O http://www.qtrpi.com/downloads/qtrpi/rpi3/$QT && unzip -qq $QT -d /opt/qtrpi && rm -f $QT +RUN git clone -q --depth 1 -b 5.7 https://code.qt.io/qt/qtvirtualkeyboard.git /opt/qtrpi/raspi/qtvirtualkeyboard && cd /opt/qtrpi/raspi/qtvirtualkeyboard && ../qt5/bin/qmake qtvirtualkeyboard.pro CONFIG+=lang-all && make && make install && rm -rf /opt/qtrpi/raspi/qtvirtualkeyboard + +RUN $GOPATH/bin/qtsetup check rpi3 +RUN $GOPATH/bin/qtsetup generate rpi3 +RUN $GOPATH/bin/qtsetup install rpi3 +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build rpi3 && rm -rf ./deploy diff --git a/qt/tool-chain/docker/sailfish/Dockerfile b/qt/tool-chain/docker/sailfish/Dockerfile new file mode 100644 index 0000000..71c898e --- /dev/null +++ b/qt/tool-chain/docker/sailfish/Dockerfile @@ -0,0 +1,81 @@ +FROM i386/ubuntu:14.04 as base + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git +RUN GO=go1.11.2.linux-386.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local +RUN /usr/local/go/bin/go get -tags=no_env github.com/peterq/pan-light/qt/cmd/... + + +FROM i386/ubuntu:14.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV SF_SDK_TOOLING_DIR /srv/mer/toolings/SailfishOS-2.2.1.18 +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_SAILFISH true + +COPY --from=base /usr/local/go /usr/local/go +COPY --from=base $GOPATH/bin $GOPATH/bin +COPY --from=base $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl && apt-get -qq clean +RUN apt-get -qq update && apt-get -y -qq purge python && apt-get -qq clean + +RUN SF_SDK_TOOLING=Jolla-latest-Sailfish_SDK_Tooling-i486.tar.bz2 && mkdir -p $SF_SDK_TOOLING_DIR && curl -sL --retry 10 --retry-delay 60 -O https://releases.sailfishos.org/sdk/latest/$SF_SDK_TOOLING && tar --numeric-owner -p -xjf $SF_SDK_TOOLING -C $SF_SDK_TOOLING_DIR && rm -f $SF_SDK_TOOLING + +RUN SF_SDK_TARGET=Jolla-latest-Sailfish_SDK_Target-i486.tar.bz2 && mkdir -p /srv/mer/targets/SailfishOS-2.2.1.18-i486 && curl -sL --retry 10 --retry-delay 60 -O https://releases.sailfishos.org/sdk/latest/$SF_SDK_TARGET && tar --numeric-owner -p -xjf $SF_SDK_TARGET -C /srv/mer/targets/SailfishOS-2.2.1.18-i486 && rm -f $SF_SDK_TARGET + +RUN SF_SDK_TARGET=Jolla-latest-Sailfish_SDK_Target-armv7hl.tar.bz2 && mkdir -p /srv/mer/targets/SailfishOS-2.2.1.18-armv7hl && curl -sL --retry 10 --retry-delay 60 -O https://releases.sailfishos.org/sdk/latest/$SF_SDK_TARGET && tar --numeric-owner -p -xjf $SF_SDK_TARGET -C /srv/mer/targets/SailfishOS-2.2.1.18-armv7hl && rm -f $SF_SDK_TARGET + +ENV PATH $SF_SDK_TOOLING_DIR/usr/bin/:$PATH + +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libmpc.so.3 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libmpfr.so.4 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libgmp.so.10 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libpthread_nonshared.a /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libc_nonshared.a /usr/lib/ + +RUN ln -s $SF_SDK_TOOLING_DIR/opt/cross/bin/i486-meego-linux-gnu-as $SF_SDK_TOOLING_DIR/opt/cross/libexec/gcc/i486-meego-linux-gnu/4.8.3/as +RUN ln -s $SF_SDK_TOOLING_DIR/opt/cross/bin/i486-meego-linux-gnu-ld $SF_SDK_TOOLING_DIR/opt/cross/libexec/gcc/i486-meego-linux-gnu/4.8.3/ld + +RUN ln -s $SF_SDK_TOOLING_DIR/opt/cross/bin/armv7hl-meego-linux-gnueabi-as $SF_SDK_TOOLING_DIR/opt/cross/libexec/gcc/armv7hl-meego-linux-gnueabi/4.8.3/as +RUN ln -s $SF_SDK_TOOLING_DIR/opt/cross/bin/armv7hl-meego-linux-gnueabi-ld $SF_SDK_TOOLING_DIR/opt/cross/libexec/gcc/armv7hl-meego-linux-gnueabi/4.8.3/ld + +RUN cd /srv/mer/targets/SailfishOS-2.2.1.18-i486/ && $SF_SDK_TOOLING_DIR/usr/bin/sb2-init -L "--sysroot=/" -C "--sysroot=/" -n -N -t / i486-meego-linux-gnu $SF_SDK_TOOLING_DIR/opt/cross/bin/i486-meego-linux-gnu-gcc + +RUN cd /srv/mer/targets/SailfishOS-2.2.1.18-i486/ && $SF_SDK_TOOLING_DIR/usr/bin/sb2-init -L "--sysroot=/" -C "--sysroot=/" -n -N -t / armv7hl-meego-linux $SF_SDK_TOOLING_DIR/opt/cross/bin/i486-meego-linux-gnu-gcc + +RUN sed -i 's/--target=$build_tgt/--target=$OPT_TARGET/g' /srv/mer/toolings/SailfishOS-2.2.1.18/usr/bin/mb2 + +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/librpmbuild.so.8 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/librpm.so.8 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/librpmio.so.8 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libdb-4.8.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/liblua-5.1.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libpython2.7.so.1.0 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libbfd-2.25.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libnss3.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libelf.so.1 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libnssutil3.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libplc4.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libplds4.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libnspr4.so /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libglib-2.0.so.0 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libpcre.so.1 /usr/lib/ +RUN ln -s $SF_SDK_TOOLING_DIR/usr/lib/libdw.so.1 /usr/lib/ + +RUN $GOPATH/bin/qtsetup prep + +RUN $GOPATH/bin/qtsetup generate sailfish +RUN $GOPATH/bin/qtsetup install sailfish +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/sailfish/listview && $GOPATH/bin/qtdeploy build sailfish && rm -rf ./deploy + +RUN $GOPATH/bin/qtsetup generate sailfish-emulator +RUN $GOPATH/bin/qtsetup install sailfish-emulator +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/sailfish/listview && $GOPATH/bin/qtdeploy build sailfish-emulator && rm -rf ./deploy diff --git a/qt/tool-chain/docker/ubports/Dockerfile.64_vivid b/qt/tool-chain/docker/ubports/Dockerfile.64_vivid new file mode 100644 index 0000000..58ef814 --- /dev/null +++ b/qt/tool-chain/docker/ubports/Dockerfile.64_vivid @@ -0,0 +1,24 @@ +FROM clickable/ubuntu-sdk:15.04-amd64 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true +ENV QT_UBPORTS true +ENV QT_UBPORTS_ARCH amd64 +ENV QT_UBPORTS_VERSION vivid + +RUN rm -rf /usr/local/go || true +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install qt*5-doc-html && apt-get -qq clean + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check ubports +RUN $GOPATH/bin/qtsetup generate ubports +RUN $GOPATH/bin/qtsetup install ubports diff --git a/qt/tool-chain/docker/ubports/Dockerfile.64_xenial b/qt/tool-chain/docker/ubports/Dockerfile.64_xenial new file mode 100644 index 0000000..012582b --- /dev/null +++ b/qt/tool-chain/docker/ubports/Dockerfile.64_xenial @@ -0,0 +1,22 @@ +FROM clickable/ubuntu-sdk:16.04-amd64 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true +ENV QT_UBPORTS true +ENV QT_UBPORTS_ARCH amd64 +ENV QT_UBPORTS_VERSION xenial + +RUN rm -rf /usr/local/go || true +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check ubports +RUN $GOPATH/bin/qtsetup generate ubports +RUN $GOPATH/bin/qtsetup install ubports diff --git a/qt/tool-chain/docker/ubports/Dockerfile.arm_vivid b/qt/tool-chain/docker/ubports/Dockerfile.arm_vivid new file mode 100644 index 0000000..edfea09 --- /dev/null +++ b/qt/tool-chain/docker/ubports/Dockerfile.arm_vivid @@ -0,0 +1,27 @@ +FROM clickable/ubuntu-sdk:15.04-armhf +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true +ENV QT_UBPORTS true +ENV QT_UBPORTS_ARCH arm +ENV QT_UBPORTS_VERSION vivid + +RUN rm -rf /usr/local/go || true +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install qt*5-doc-html && apt-get -qq clean + +RUN ln -s /usr/lib/x86_64-linux-gnu/qt5/bin/rcc /usr/lib/arm-linux-gnueabihf/qt5/bin/ +RUN ln -s /usr/lib/x86_64-linux-gnu/qt5/bin/moc /usr/lib/arm-linux-gnueabihf/qt5/bin/ + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check ubports +RUN $GOPATH/bin/qtsetup generate ubports +RUN $GOPATH/bin/qtsetup install ubports diff --git a/qt/tool-chain/docker/ubports/Dockerfile.arm_xenial b/qt/tool-chain/docker/ubports/Dockerfile.arm_xenial new file mode 100644 index 0000000..70f4d76 --- /dev/null +++ b/qt/tool-chain/docker/ubports/Dockerfile.arm_xenial @@ -0,0 +1,24 @@ +FROM clickable/ubuntu-sdk:16.04-armhf +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/local/go/bin:$PATH +ENV QT_DOCKER true +ENV QT_PKG_CONFIG true +ENV QT_UBPORTS true +ENV QT_UBPORTS_ARCH arm +ENV QT_UBPORTS_VERSION xenial + +RUN rm -rf /usr/local/go || true +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt + +RUN ln -s /usr/lib/arm-linux-gnueabihf/qt5/qt.conf /usr/lib/qt5/bin/ + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check ubports +RUN $GOPATH/bin/qtsetup generate ubports +RUN $GOPATH/bin/qtsetup install ubports diff --git a/qt/tool-chain/docker/windows_32_shared/Dockerfile b/qt/tool-chain/docker/windows_32_shared/Dockerfile new file mode 100644 index 0000000..418c6dd --- /dev/null +++ b/qt/tool-chain/docker/windows_32_shared/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH 386 +ENV QT_MXE_STATIC false + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:windows_32_shared_base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_32_shared/Dockerfile.base b/qt/tool-chain/docker/windows_32_shared/Dockerfile.base new file mode 100644 index 0000000..6802414 --- /dev/null +++ b/qt/tool-chain/docker/windows_32_shared/Dockerfile.base @@ -0,0 +1,8 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates git +RUN git clone -q --depth 1 https://github.com/mxe/mxe.git /usr/lib/mxe +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install autoconf automake autopoint bash bison bzip2 flex g++ g++-multilib gettext git gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libssl-dev libtool-bin libxml-parser-perl make openssl p7zip-full patch perl pkg-config python ruby scons sed unzip wget xz-utils lzip + +RUN cd /usr/lib/mxe && make MXE_TARGETS='i686-w64-mingw32.shared' qt5 && rm -rf /usr/lib/mxe/log && rm -rf /usr/lib/mxe/pkg \ No newline at end of file diff --git a/qt/tool-chain/docker/windows_32_static/Dockerfile b/qt/tool-chain/docker/windows_32_static/Dockerfile new file mode 100644 index 0000000..c1e3306 --- /dev/null +++ b/qt/tool-chain/docker/windows_32_static/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH 386 +ENV QT_MXE_STATIC true + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:windows_32_static_base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_32_static/Dockerfile.base b/qt/tool-chain/docker/windows_32_static/Dockerfile.base new file mode 100644 index 0000000..0418c29 --- /dev/null +++ b/qt/tool-chain/docker/windows_32_static/Dockerfile.base @@ -0,0 +1,11 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates git +RUN git clone -q --depth 1 https://github.com/mxe/mxe.git /usr/lib/mxe +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install autoconf automake autopoint bash bison bzip2 flex g++ g++-multilib gettext git gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libssl-dev libtool-bin libxml-parser-perl make openssl p7zip-full patch perl pkg-config python ruby scons sed unzip wget xz-utils lzip + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install curl +RUN curl -sL --retry 10 --retry-delay 60 https://github.com/qt/qtbase/commit/826b09f0c507fe5321a8534054a4f0b7bdd2699b.patch > /usr/lib/mxe/src/qtbase-2-fixes.patch + +RUN cd /usr/lib/mxe && make MXE_TARGETS='i686-w64-mingw32.static' qt5 && rm -rf /usr/lib/mxe/log && rm -rf /usr/lib/mxe/pkg diff --git a/qt/tool-chain/docker/windows_64_shared/Dockerfile b/qt/tool-chain/docker/windows_64_shared/Dockerfile new file mode 100644 index 0000000..3ce7a88 --- /dev/null +++ b/qt/tool-chain/docker/windows_64_shared/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH amd64 +ENV QT_MXE_STATIC false + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:windows_64_shared_base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_64_shared/Dockerfile.base b/qt/tool-chain/docker/windows_64_shared/Dockerfile.base new file mode 100644 index 0000000..fab4a7f --- /dev/null +++ b/qt/tool-chain/docker/windows_64_shared/Dockerfile.base @@ -0,0 +1,8 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates git +RUN git clone -q --depth 1 https://github.com/mxe/mxe.git /usr/lib/mxe +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install autoconf automake autopoint bash bison bzip2 flex g++ g++-multilib gettext git gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libssl-dev libtool-bin libxml-parser-perl make openssl p7zip-full patch perl pkg-config python ruby scons sed unzip wget xz-utils lzip + +RUN cd /usr/lib/mxe && make MXE_TARGETS='x86_64-w64-mingw32.shared' qt5 && rm -rf /usr/lib/mxe/log && rm -rf /usr/lib/mxe/pkg diff --git a/qt/tool-chain/docker/windows_64_static/Dockerfile b/qt/tool-chain/docker/windows_64_static/Dockerfile new file mode 100644 index 0000000..8d534f2 --- /dev/null +++ b/qt/tool-chain/docker/windows_64_static/Dockerfile @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH amd64 +ENV QT_MXE_STATIC true + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=therecipe/qt:windows_64_static_base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_64_static/Dockerfile.base b/qt/tool-chain/docker/windows_64_static/Dockerfile.base new file mode 100644 index 0000000..b679311 --- /dev/null +++ b/qt/tool-chain/docker/windows_64_static/Dockerfile.base @@ -0,0 +1,11 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates git +RUN git clone -q --depth 1 https://github.com/mxe/mxe.git /usr/lib/mxe +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install autoconf automake autopoint bash bison bzip2 flex g++ g++-multilib gettext git gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libssl-dev libtool-bin libxml-parser-perl make openssl p7zip-full patch perl pkg-config python ruby scons sed unzip wget xz-utils lzip + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install curl +RUN curl -sL --retry 10 --retry-delay 60 https://github.com/qt/qtbase/commit/826b09f0c507fe5321a8534054a4f0b7bdd2699b.patch > /usr/lib/mxe/src/qtbase-2-fixes.patch + +RUN cd /usr/lib/mxe && make MXE_TARGETS='x86_64-w64-mingw32.static' qt5 && rm -rf /usr/lib/mxe/log && rm -rf /usr/lib/mxe/pkg diff --git a/qt/tool-chain/docker/windows_legacy/Dockerfile.32_shared b/qt/tool-chain/docker/windows_legacy/Dockerfile.32_shared new file mode 100644 index 0000000..acf5390 --- /dev/null +++ b/qt/tool-chain/docker/windows_legacy/Dockerfile.32_shared @@ -0,0 +1,30 @@ +FROM ubuntu:16.04 as base + +RUN echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" | tee --append /etc/apt/sources.list.d/mxeapt.list > /dev/null && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB && apt-get -qq update && apt-get --no-install-recommends -qq -y install mxe-i686-w64-mingw32.shared-qt3d mxe-i686-w64-mingw32.shared-qtactiveqt mxe-i686-w64-mingw32.shared-qtbase mxe-i686-w64-mingw32.shared-qtcanvas3d mxe-i686-w64-mingw32.shared-qtcharts mxe-i686-w64-mingw32.shared-qtconnectivity mxe-i686-w64-mingw32.shared-qtdatavis3d mxe-i686-w64-mingw32.shared-qtdeclarative mxe-i686-w64-mingw32.shared-qtgamepad mxe-i686-w64-mingw32.shared-qtgraphicaleffects mxe-i686-w64-mingw32.shared-qtimageformats mxe-i686-w64-mingw32.shared-qtlocation mxe-i686-w64-mingw32.shared-qtmultimedia mxe-i686-w64-mingw32.shared-qtofficeopenxml mxe-i686-w64-mingw32.shared-qtpurchasing mxe-i686-w64-mingw32.shared-qtquickcontrols mxe-i686-w64-mingw32.shared-qtquickcontrols2 mxe-i686-w64-mingw32.shared-qtscript mxe-i686-w64-mingw32.shared-qtscxml mxe-i686-w64-mingw32.shared-qtsensors mxe-i686-w64-mingw32.shared-qtserialbus mxe-i686-w64-mingw32.shared-qtserialport mxe-i686-w64-mingw32.shared-qtservice mxe-i686-w64-mingw32.shared-qtsvg mxe-i686-w64-mingw32.shared-qtsystems mxe-i686-w64-mingw32.shared-qttools mxe-i686-w64-mingw32.shared-qttranslations mxe-i686-w64-mingw32.shared-qtvirtualkeyboard mxe-i686-w64-mingw32.shared-qtwebchannel mxe-i686-w64-mingw32.shared-qtwebkit mxe-i686-w64-mingw32.shared-qtwebsockets mxe-i686-w64-mingw32.shared-qtwinextras mxe-i686-w64-mingw32.shared-qtxlsxwriter mxe-i686-w64-mingw32.shared-qtxmlpatterns + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_API 5.8.0 +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH 386 +ENV QT_MXE_STATIC false + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_legacy/Dockerfile.32_static b/qt/tool-chain/docker/windows_legacy/Dockerfile.32_static new file mode 100644 index 0000000..32f791b --- /dev/null +++ b/qt/tool-chain/docker/windows_legacy/Dockerfile.32_static @@ -0,0 +1,30 @@ +FROM ubuntu:16.04 as base + +RUN echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" | tee --append /etc/apt/sources.list.d/mxeapt.list > /dev/null && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB && apt-get -qq update && apt-get --no-install-recommends -qq -y install mxe-i686-w64-mingw32.static-qt3d mxe-i686-w64-mingw32.static-qtactiveqt mxe-i686-w64-mingw32.static-qtbase mxe-i686-w64-mingw32.static-qtcanvas3d mxe-i686-w64-mingw32.static-qtcharts mxe-i686-w64-mingw32.static-qtconnectivity mxe-i686-w64-mingw32.static-qtdatavis3d mxe-i686-w64-mingw32.static-qtdeclarative mxe-i686-w64-mingw32.static-qtgamepad mxe-i686-w64-mingw32.static-qtgraphicaleffects mxe-i686-w64-mingw32.static-qtimageformats mxe-i686-w64-mingw32.static-qtlocation mxe-i686-w64-mingw32.static-qtmultimedia mxe-i686-w64-mingw32.static-qtofficeopenxml mxe-i686-w64-mingw32.static-qtpurchasing mxe-i686-w64-mingw32.static-qtquickcontrols mxe-i686-w64-mingw32.static-qtquickcontrols2 mxe-i686-w64-mingw32.static-qtscript mxe-i686-w64-mingw32.static-qtscxml mxe-i686-w64-mingw32.static-qtsensors mxe-i686-w64-mingw32.static-qtserialbus mxe-i686-w64-mingw32.static-qtserialport mxe-i686-w64-mingw32.static-qtservice mxe-i686-w64-mingw32.static-qtsvg mxe-i686-w64-mingw32.static-qtsystems mxe-i686-w64-mingw32.static-qttools mxe-i686-w64-mingw32.static-qttranslations mxe-i686-w64-mingw32.static-qtvirtualkeyboard mxe-i686-w64-mingw32.static-qtwebchannel mxe-i686-w64-mingw32.static-qtwebsockets mxe-i686-w64-mingw32.static-qtwinextras mxe-i686-w64-mingw32.static-qtxlsxwriter mxe-i686-w64-mingw32.static-qtxmlpatterns + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_API 5.8.0 +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH 386 +ENV QT_MXE_STATIC true + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_legacy/Dockerfile.64_shared b/qt/tool-chain/docker/windows_legacy/Dockerfile.64_shared new file mode 100644 index 0000000..17f61bf --- /dev/null +++ b/qt/tool-chain/docker/windows_legacy/Dockerfile.64_shared @@ -0,0 +1,30 @@ +FROM ubuntu:16.04 as base + +RUN echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" | tee --append /etc/apt/sources.list.d/mxeapt.list > /dev/null && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB && apt-get -qq update && apt-get --no-install-recommends -qq -y install mxe-x86-64-w64-mingw32.shared-qt3d mxe-x86-64-w64-mingw32.shared-qtactiveqt mxe-x86-64-w64-mingw32.shared-qtbase mxe-x86-64-w64-mingw32.shared-qtcanvas3d mxe-x86-64-w64-mingw32.shared-qtcharts mxe-x86-64-w64-mingw32.shared-qtconnectivity mxe-x86-64-w64-mingw32.shared-qtdatavis3d mxe-x86-64-w64-mingw32.shared-qtdeclarative mxe-x86-64-w64-mingw32.shared-qtgamepad mxe-x86-64-w64-mingw32.shared-qtgraphicaleffects mxe-x86-64-w64-mingw32.shared-qtimageformats mxe-x86-64-w64-mingw32.shared-qtlocation mxe-x86-64-w64-mingw32.shared-qtmultimedia mxe-x86-64-w64-mingw32.shared-qtofficeopenxml mxe-x86-64-w64-mingw32.shared-qtpurchasing mxe-x86-64-w64-mingw32.shared-qtquickcontrols mxe-x86-64-w64-mingw32.shared-qtquickcontrols2 mxe-x86-64-w64-mingw32.shared-qtscript mxe-x86-64-w64-mingw32.shared-qtscxml mxe-x86-64-w64-mingw32.shared-qtsensors mxe-x86-64-w64-mingw32.shared-qtserialbus mxe-x86-64-w64-mingw32.shared-qtserialport mxe-x86-64-w64-mingw32.shared-qtservice mxe-x86-64-w64-mingw32.shared-qtsvg mxe-x86-64-w64-mingw32.shared-qtsystems mxe-x86-64-w64-mingw32.shared-qttools mxe-x86-64-w64-mingw32.shared-qttranslations mxe-x86-64-w64-mingw32.shared-qtvirtualkeyboard mxe-x86-64-w64-mingw32.shared-qtwebchannel mxe-x86-64-w64-mingw32.shared-qtwebkit mxe-x86-64-w64-mingw32.shared-qtwebsockets mxe-x86-64-w64-mingw32.shared-qtwinextras mxe-x86-64-w64-mingw32.shared-qtxlsxwriter mxe-x86-64-w64-mingw32.shared-qtxmlpatterns + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_API 5.8.0 +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH amd64 +ENV QT_MXE_STATIC false + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/windows_legacy/Dockerfile.64_static b/qt/tool-chain/docker/windows_legacy/Dockerfile.64_static new file mode 100644 index 0000000..7c2ba9a --- /dev/null +++ b/qt/tool-chain/docker/windows_legacy/Dockerfile.64_static @@ -0,0 +1,30 @@ +FROM ubuntu:16.04 as base + +RUN echo "deb http://pkg.mxe.cc/repos/apt/debian wheezy main" | tee --append /etc/apt/sources.list.d/mxeapt.list > /dev/null && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D43A795B73B16ABE9643FE1AFD8FFF16DB45C6AB && apt-get -qq update && apt-get --no-install-recommends -qq -y install mxe-x86-64-w64-mingw32.static-qt3d mxe-x86-64-w64-mingw32.static-qtactiveqt mxe-x86-64-w64-mingw32.static-qtbase mxe-x86-64-w64-mingw32.static-qtcanvas3d mxe-x86-64-w64-mingw32.static-qtcharts mxe-x86-64-w64-mingw32.static-qtconnectivity mxe-x86-64-w64-mingw32.static-qtdatavis3d mxe-x86-64-w64-mingw32.static-qtdeclarative mxe-x86-64-w64-mingw32.static-qtgamepad mxe-x86-64-w64-mingw32.static-qtgraphicaleffects mxe-x86-64-w64-mingw32.static-qtimageformats mxe-x86-64-w64-mingw32.static-qtlocation mxe-x86-64-w64-mingw32.static-qtmultimedia mxe-x86-64-w64-mingw32.static-qtofficeopenxml mxe-x86-64-w64-mingw32.static-qtpurchasing mxe-x86-64-w64-mingw32.static-qtquickcontrols mxe-x86-64-w64-mingw32.static-qtquickcontrols2 mxe-x86-64-w64-mingw32.static-qtscript mxe-x86-64-w64-mingw32.static-qtscxml mxe-x86-64-w64-mingw32.static-qtsensors mxe-x86-64-w64-mingw32.static-qtserialbus mxe-x86-64-w64-mingw32.static-qtserialport mxe-x86-64-w64-mingw32.static-qtservice mxe-x86-64-w64-mingw32.static-qtsvg mxe-x86-64-w64-mingw32.static-qtsystems mxe-x86-64-w64-mingw32.static-qttools mxe-x86-64-w64-mingw32.static-qttranslations mxe-x86-64-w64-mingw32.static-qtvirtualkeyboard mxe-x86-64-w64-mingw32.static-qtwebchannel mxe-x86-64-w64-mingw32.static-qtwebsockets mxe-x86-64-w64-mingw32.static-qtwinextras mxe-x86-64-w64-mingw32.static-qtxlsxwriter mxe-x86-64-w64-mingw32.static-qtxmlpatterns + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/work +ENV PATH /usr/lib/mxe/usr/bin:/usr/local/go/bin:$PATH +ENV QT_API 5.8.0 +ENV QT_DIR /opt/Qt +ENV QT_DOCKER true +ENV QT_MXE true +ENV QT_MXE_ARCH amd64 +ENV QT_MXE_STATIC true + +COPY --from=therecipe/qt:linux /usr/local/go /usr/local/go +COPY --from=therecipe/qt:linux $GOPATH/bin $GOPATH/bin +COPY --from=therecipe/qt:linux $GOPATH/src/github.com/peterq/pan-light/qt $GOPATH/src/github.com/peterq/pan-light/qt +COPY --from=therecipe/qt:linux /opt/Qt/5.12.0/gcc_64/include /opt/Qt/5.12.0/gcc_64/include +COPY --from=base /usr/lib/mxe /usr/lib/mxe + +RUN $GOPATH/bin/qtsetup prep +RUN $GOPATH/bin/qtsetup check windows +RUN $GOPATH/bin/qtsetup generate windows +RUN $GOPATH/bin/qtsetup install windows +RUN cd $GOPATH/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && $GOPATH/bin/qtdeploy build windows && rm -rf ./deploy diff --git a/qt/tool-chain/docker/wine/Dockerfile b/qt/tool-chain/docker/wine/Dockerfile new file mode 100644 index 0000000..86389bd --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile @@ -0,0 +1,52 @@ +FROM therecipe/qt:wine_base as base + +RUN QT=qt-opensource-windows-x86-5.12.0.exe && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/qt/5.12/5.12.0/$QT \ + && QT_QPA_PLATFORM=minimal xvfb-run wine Z:\\$QT --script=C:\\gopath\\src\\github.com\\therecipe\\qt\\internal\\ci\\iscript.qs WINDOWS=true && rm -f $QT +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.12.0/5.12.0/mingw73_64 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.12.0/5.12.0/mingw73_64 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.12.0/5.12.0/mingw73_64 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/Qt/Qt5.12.0/5.12.0/mingw73_64 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN find $HOME/.wine/drive_c/Qt/Qt5.12.0/Docs -type f ! -name "*.index" -delete + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\Qt\\Qt5.12.0\\Tools\\mingw730_64\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.12.0/5.12.0/mingw73_64 $HOME/.wine/drive_c/Qt/Qt5.12.0/5.12.0/mingw73_64 +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.12.0/Docs $HOME/.wine/drive_c/Qt/Qt5.12.0/Docs +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.12.0/Licenses $HOME/.wine/drive_c/Qt/Qt5.12.0/Licenses +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.12.0/Tools/mingw730_64 $HOME/.wine/drive_c/Qt/Qt5.12.0/Tools/mingw730_64 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.32_shared b/qt/tool-chain/docker/wine/Dockerfile.32_shared new file mode 100644 index 0000000..cd8bf56 --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.32_shared @@ -0,0 +1,105 @@ +FROM therecipe/qt:wine_base as base + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install p7zip +RUN GCC=i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z && cd $HOME/.wine/drive_c && curl -sL --retry 10 --retry-delay 60 -O https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf/$GCC && p7zip -d $GCC && rm -f $GCC + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install xz-utils && apt-get -qq clean +RUN REPO=http://repo.msys2.org/mingw/i686 && curl -sL --retry 10 --retry-delay 60 -o repo $REPO + +ENV REPO https://sourceforge.net/projects/msys2/files/REPOS/MINGW/i686 + +RUN mkdir -p $HOME/.wine/drive_c/msys64 +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-qt5-5.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-gcc-libs-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-qtbinpatcher-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-z3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-assimp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-dbus-1.12.1.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-fontconfig-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-freetype-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-harfbuzz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-jasper-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libjpeg-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libmng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libpng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libtiff-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libxml2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-libxslt-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libwebp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-openssl-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-openal-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-pcre2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-sqlite3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-vulkan-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-xpm-nox-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-zlib-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-icu-6.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libmariadbclient-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-firebird2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-postgresql-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libiconv-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-pcre-8.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-bzip2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-glib2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-gettext-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-xz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-graphite2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libwinpthread-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN ln -s /home/user/.wine/drive_c/mingw32/bin/objdump.exe $HOME/.wine/drive_c/msys64/mingw32/bin/ +RUN cd $HOME/.wine/drive_c/msys64/mingw32/bin && wine qtbinpatcher.exe --nobackup + +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw32 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw32 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw32 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/msys64/mingw32 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw32/qt5/share/qt5/doc +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw32/qt5/share/qt5/examples + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\mingw32\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/mingw32 $HOME/.wine/drive_c/mingw32 +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/bin $HOME/.wine/drive_c/msys64/mingw32/bin +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/include $HOME/.wine/drive_c/msys64/mingw32/include +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/lib $HOME/.wine/drive_c/msys64/mingw32/lib +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/share/qt5 $HOME/.wine/drive_c/msys64/mingw32/share/qt5 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_MSYS2"="true"\n\ +"QT_MSYS2_ARCH"="386"\n\ +"QT_MSYS2_STATIC"="false"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.32_static b/qt/tool-chain/docker/wine/Dockerfile.32_static new file mode 100644 index 0000000..720ad0f --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.32_static @@ -0,0 +1,130 @@ +FROM therecipe/qt:wine_base as base + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install p7zip +RUN GCC=i686-8.1.0-release-posix-dwarf-rt_v6-rev0.7z && cd $HOME/.wine/drive_c && curl -sL --retry 10 --retry-delay 60 -O https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/8.1.0/threads-posix/dwarf/$GCC && p7zip -d $GCC && rm -f $GCC + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install xz-utils && apt-get -qq clean +RUN REPO=http://repo.msys2.org/mingw/i686 && curl -sL --retry 10 --retry-delay 60 -o repo $REPO + +ENV REPO https://sourceforge.net/projects/msys2/files/REPOS/MINGW/i686 + +RUN mkdir -p $HOME/.wine/drive_c/msys64 +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-qt5-s.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-gcc-libs-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-qtbinpatcher-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-z3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-assimp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-dbus-1.12.1.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-fontconfig-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-freetype-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-harfbuzz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-jasper-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libjpeg-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libmng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libpng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libtiff-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libxml2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-libxslt-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libwebp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-openssl-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-openal-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-pcre2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-sqlite3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-vulkan-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-xpm-nox-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-zlib-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-icu-6.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libmariadbclient-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-firebird2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-postgresql-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libiconv-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-pcre-8.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-bzip2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-glib2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-gettext-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-xz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-graphite2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-libwinpthread-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-gcc-7.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-gmp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-binutils-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-crt-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-headers-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-isl-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-mpc-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-i686-windows-default-manifest-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-i686-winpthreads-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN ln -s $HOME/.wine/drive_c/msys64/mingw32/lib/libdbus-1.a $HOME/.wine/drive_c/msys64/mingw32/qt5-static/lib/libdbus-1.a +RUN ln -s $HOME/.wine/drive_c/msys64/mingw32/lib/libcrypto.a $HOME/.wine/drive_c/msys64/mingw32/qt5-static/lib/libcrypto.a +RUN ln -s $HOME/.wine/drive_c/msys64/mingw32/lib/libjasper.a $HOME/.wine/drive_c/msys64/mingw32/qt5-static/lib/libjasper.a +RUN ln -s $HOME/.wine/drive_c/msys64/mingw32/lib/libssl.a $HOME/.wine/drive_c/msys64/mingw32/qt5-static/lib/libssl.a + +RUN cd $HOME/.wine/drive_c/msys64/mingw32/qt5-static/bin && wine ../../bin/qtbinpatcher.exe --nobackup + +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw32 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw32 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw32 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/msys64/mingw32 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw32/qt5-static/share/qt5/doc +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw32/qt5-static/share/qt5/examples + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\mingw32\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/mingw32 $HOME/.wine/drive_c/mingw32 + +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/qt5-static/bin $HOME/.wine/drive_c/msys64/mingw32/qt5-static/bin +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/qt5-static/include $HOME/.wine/drive_c/msys64/mingw32/qt5-static/include +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/qt5-static/lib $HOME/.wine/drive_c/msys64/mingw32/qt5-static/lib +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/qt5-static/share/qt5 $HOME/.wine/drive_c/msys64/mingw32/qt5-static/share/qt5 + +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/bin $HOME/.wine/drive_c/msys64/mingw32/bin +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/include $HOME/.wine/drive_c/msys64/mingw32/include +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/lib $HOME/.wine/drive_c/msys64/mingw32/lib +COPY --from=base $HOME/.wine/drive_c/msys64/mingw32/i686-w64-mingw32 $HOME/.wine/drive_c/msys64/mingw32/i686-w64-mingw32 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install patch && apt-get -qq clean +ADD https://raw.githubusercontent.com/therecipe/qt/master/internal/docker/wine/rspfile.patch $HOME/.wine/drive_c/go/rspfile.patch +RUN cd $HOME/.wine/drive_c/go && patch -p 1 < rspfile.patch +RUN wine go install cmd/go cmd/link + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_MSYS2"="true"\n\ +"QT_MSYS2_ARCH"="386"\n\ +"QT_MSYS2_STATIC"="true"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.56 b/qt/tool-chain/docker/wine/Dockerfile.56 new file mode 100644 index 0000000..7538e72 --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.56 @@ -0,0 +1,51 @@ +FROM therecipe/qt:wine_base as base + +RUN QT=qt-opensource-windows-x86-mingw492-5.6.3.exe && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/qt/5.6/5.6.3/$QT \ + && QT_QPA_PLATFORM=minimal xvfb-run wine Z:\\$QT --script=C:\\gopath\\src\\github.com\\therecipe\\qt\\internal\\ci\\iscript.qs WINDOWS=true VERSION=563 && rm -f $QT +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN find $HOME/.wine/drive_c/Qt/Qt5.6.3/Docs -type f ! -name "*.index" -delete + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\Qt\\Qt5.6.3\\Tools\\mingw492_32\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.6.3/Docs $HOME/.wine/drive_c/Qt/Qt5.6.3/Docs +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.6.3/Tools/mingw492_32 $HOME/.wine/drive_c/Qt/Qt5.6.3/Tools/mingw492_32 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_VERSION"="5.6.3"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.56_xp b/qt/tool-chain/docker/wine/Dockerfile.56_xp new file mode 100644 index 0000000..b18f484 --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.56_xp @@ -0,0 +1,51 @@ +FROM therecipe/qt:wine_base_xp as base + +RUN QT=qt-opensource-windows-x86-mingw492-5.6.3.exe && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/qt/5.6/5.6.3/$QT \ + && QT_QPA_PLATFORM=minimal xvfb-run wine Z:\\$QT --script=C:\\gopath\\src\\github.com\\therecipe\\qt\\internal\\ci\\iscript.qs WINDOWS=true VERSION=563 && rm -f $QT +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN find $HOME/.wine/drive_c/Qt/Qt5.6.3/Docs -type f ! -name "*.index" -delete + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\Qt\\Qt5.6.3\\Tools\\mingw492_32\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 $HOME/.wine/drive_c/Qt/Qt5.6.3/5.6.3/mingw49_32 +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.6.3/Docs $HOME/.wine/drive_c/Qt/Qt5.6.3/Docs +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.6.3/Tools/mingw492_32 $HOME/.wine/drive_c/Qt/Qt5.6.3/Tools/mingw492_32 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_VERSION"="5.6.3"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.59 b/qt/tool-chain/docker/wine/Dockerfile.59 new file mode 100644 index 0000000..e15c6ed --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.59 @@ -0,0 +1,51 @@ +FROM therecipe/qt:wine_base as base + +RUN QT=qt-opensource-windows-x86-5.9.6.exe && curl -sL --retry 10 --retry-delay 60 -O https://download.qt.io/official_releases/qt/5.9/5.9.6/$QT \ + && QT_QPA_PLATFORM=minimal xvfb-run wine Z:\\$QT --script=C:\\gopath\\src\\github.com\\therecipe\\qt\\internal\\ci\\iscript.qs WINDOWS=true VERSION=596 && rm -f $QT +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.9.6/5.9.6/mingw53_32 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.9.6/5.9.6/mingw53_32 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/Qt/Qt5.9.6/5.9.6/mingw53_32 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/Qt/Qt5.9.6/5.9.6/mingw53_32 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN find $HOME/.wine/drive_c/Qt/Qt5.9.6/Docs -type f ! -name "*.index" -delete + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\Qt\\Qt5.9.6\\Tools\\mingw530_32\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.9.6/5.9.6/mingw53_32 $HOME/.wine/drive_c/Qt/Qt5.9.6/5.9.6/mingw53_32 +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.9.6/Docs $HOME/.wine/drive_c/Qt/Qt5.9.6/Docs +COPY --from=base $HOME/.wine/drive_c/Qt/Qt5.9.6/Tools/mingw530_32 $HOME/.wine/drive_c/Qt/Qt5.9.6/Tools/mingw530_32 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_VERSION"="5.9.6"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.64_shared b/qt/tool-chain/docker/wine/Dockerfile.64_shared new file mode 100644 index 0000000..853703b --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.64_shared @@ -0,0 +1,105 @@ +FROM therecipe/qt:wine_base as base + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install p7zip +RUN GCC=x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z && cd $HOME/.wine/drive_c && curl -sL --retry 10 --retry-delay 60 -O https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/$GCC && p7zip -d $GCC && rm -f $GCC + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install xz-utils && apt-get -qq clean +RUN REPO=http://repo.msys2.org/mingw/x86_64 && curl -sL --retry 10 --retry-delay 60 -o repo $REPO + +ENV REPO https://sourceforge.net/projects/msys2/files/REPOS/MINGW/x86_64 + +RUN mkdir -p $HOME/.wine/drive_c/msys64 +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-qt5-5.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-gcc-libs-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-qtbinpatcher-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-z3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-assimp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-dbus-1.12.1.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-fontconfig-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-freetype-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-harfbuzz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-jasper-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libjpeg-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libmng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libpng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libtiff-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libxml2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-libxslt-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libwebp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-openssl-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-openal-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-pcre2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-sqlite3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-vulkan-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-xpm-nox-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-zlib-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-icu-6.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libmariadbclient-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-firebird2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-postgresql-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libiconv-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-pcre-8.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-bzip2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-glib2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-gettext-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-xz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-graphite2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libwinpthread-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN ln -s /home/user/.wine/drive_c/mingw64/bin/objdump.exe $HOME/.wine/drive_c/msys64/mingw64/bin/ +RUN cd $HOME/.wine/drive_c/msys64/mingw64/bin && wine qtbinpatcher.exe --nobackup + +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw64 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw64 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw64 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/msys64/mingw64 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw64/qt5/share/qt5/doc +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw64/qt5/share/qt5/examples + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\mingw64\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/mingw64 $HOME/.wine/drive_c/mingw64 +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/bin $HOME/.wine/drive_c/msys64/mingw64/bin +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/include $HOME/.wine/drive_c/msys64/mingw64/include +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/lib $HOME/.wine/drive_c/msys64/mingw64/lib +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/share/qt5 $HOME/.wine/drive_c/msys64/mingw64/share/qt5 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_MSYS2"="true"\n\ +"QT_MSYS2_ARCH"="amd64"\n\ +"QT_MSYS2_STATIC"="false"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work diff --git a/qt/tool-chain/docker/wine/Dockerfile.64_static b/qt/tool-chain/docker/wine/Dockerfile.64_static new file mode 100644 index 0000000..be4d694 --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.64_static @@ -0,0 +1,131 @@ +FROM therecipe/qt:wine_base as base + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install p7zip +RUN GCC=x86_64-8.1.0-release-posix-seh-rt_v6-rev0.7z && cd $HOME/.wine/drive_c && curl -sL --retry 10 --retry-delay 60 -O https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/threads-posix/seh/$GCC && p7zip -d $GCC && rm -f $GCC + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install xz-utils && apt-get -qq clean +RUN REPO=http://repo.msys2.org/mingw/x86_64 && curl -sL --retry 10 --retry-delay 60 -o repo $REPO + +ENV REPO https://sourceforge.net/projects/msys2/files/REPOS/MINGW/x86_64 + +RUN mkdir -p $HOME/.wine/drive_c/msys64 +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-qt5-s.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-gcc-libs-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-qtbinpatcher-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-z3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-assimp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-dbus-1.12.1.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-fontconfig-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-freetype-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-harfbuzz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-jasper-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libjpeg-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libmng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libpng-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libtiff-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libxml2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-libxslt-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libwebp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-openssl-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-openal-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-pcre2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-sqlite3-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-vulkan-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-xpm-nox-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-zlib-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-icu-6.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libmariadbclient-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-firebird2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-postgresql-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libiconv-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-pcre-8.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-bzip2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-glib2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-gettext-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-xz-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-graphite2-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-libwinpthread-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-gcc-8.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-gmp-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-binutils-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-crt-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-headers-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-isl-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-mpc-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2017 | grep -m 1 -o -P 'mingw-w64-x86_64-windows-default-manifest-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG +RUN PKG=$(tac repo | grep 2018 | grep -m 1 -o -P 'mingw-w64-x86_64-winpthreads-.*tar.xz(?=\")') && curl -sL --retry 10 --retry-delay 60 -O $REPO/$PKG && tar -xf $PKG -C $HOME/.wine/drive_c/msys64 && rm -f $PKG + +RUN ln -s $HOME/.wine/drive_c/msys64/mingw64/lib/libdbus-1.a $HOME/.wine/drive_c/msys64/mingw64/qt5-static/lib/libdbus-1.a +RUN ln -s $HOME/.wine/drive_c/msys64/mingw64/lib/libcrypto.a $HOME/.wine/drive_c/msys64/mingw64/qt5-static/lib/libcrypto.a +RUN ln -s $HOME/.wine/drive_c/msys64/mingw64/lib/libjasper.a $HOME/.wine/drive_c/msys64/mingw64/qt5-static/lib/libjasper.a +RUN ln -s $HOME/.wine/drive_c/msys64/mingw64/lib/libssl.a $HOME/.wine/drive_c/msys64/mingw64/qt5-static/lib/libssl.a + +RUN cd $HOME/.wine/drive_c/msys64/mingw64/qt5-static/bin && wine ../../bin/qtbinpatcher.exe --nobackup + +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw64 -name "*d.a" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw64 -name "*d.dll" -exec grep -l "gnu_debug" {} \+) +RUN rm -f $(find $HOME/.wine/drive_c/msys64/mingw64 -name "*d.dll.a" -exec grep -l "gnu_debug" {} \+) +RUN find $HOME/.wine/drive_c/msys64/mingw64 -name "*.exe" -size +7M -maxdepth 4 -delete +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw64/qt5-static/share/qt5/doc +RUN rm -rf $HOME/.wine/drive_c/msys64/mingw64/qt5-static/share/qt5/examples + + +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH C:\\gopath +ENV WINEDEBUG -all +ENV WINEPATH C:\\mingw64\\bin;C:\\go\\bin + +COPY --from=base $HOME/.wine/drive_c/go $HOME/.wine/drive_c/go +COPY --from=base $HOME/.wine/drive_c/gopath/bin $HOME/.wine/drive_c/gopath/bin +COPY --from=base $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt +COPY --from=base $HOME/.wine/drive_c/mingw64 $HOME/.wine/drive_c/mingw64 + +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/qt5-static/bin $HOME/.wine/drive_c/msys64/mingw64/qt5-static/bin +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/qt5-static/include $HOME/.wine/drive_c/msys64/mingw64/qt5-static/include +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/qt5-static/lib $HOME/.wine/drive_c/msys64/mingw64/qt5-static/lib +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/qt5-static/share/qt5 $HOME/.wine/drive_c/msys64/mingw64/qt5-static/share/qt5 + +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/bin $HOME/.wine/drive_c/msys64/mingw64/bin +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/include $HOME/.wine/drive_c/msys64/mingw64/include +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/lib $HOME/.wine/drive_c/msys64/mingw64/lib +COPY --from=base $HOME/.wine/drive_c/msys64/mingw64/x86_64-w64-mingw32 $HOME/.wine/drive_c/msys64/mingw64/x86_64-w64-mingw32 + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" wineboot && wineserver -w + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install patch && apt-get -qq clean +ADD https://raw.githubusercontent.com/therecipe/qt/master/internal/docker/wine/rspfile.patch $HOME/.wine/drive_c/go/rspfile.patch +RUN cd $HOME/.wine/drive_c/go && patch -p 1 < rspfile.patch +RUN wine go install cmd/go cmd/link + +RUN echo 'REGEDIT4\n\n\ +[HKEY_CURRENT_USER\Environment]\n\ +"QT_DOCKER"="true"\n\ +"QT_MSYS2"="true"\n\ +"QT_MSYS2_ARCH"="amd64"\n\ +"QT_MSYS2_STATIC"="true"' > env.reg && regedit Z:\\env.reg && wineserver -w && rm -f env.reg + +RUN wine $GOPATH\\bin\\qtsetup prep +RUN wine $GOPATH\\bin\\qtsetup check +RUN wine $GOPATH\\bin\\qtsetup generate +RUN cd $HOME/.wine/drive_c/gopath/src/github.com/peterq/pan-light/qt/internal/examples/widgets/line_edits && wine qtdeploy build windows && rm -rf ./deploy + +RUN echo "#!/bin/bash\nexport GOPATH=\$(winepath -0 -w \$(echo \$GOPATH | sed -e 's/:/ /g') | sed -e 's/Z:/;Z:/2g')" > /usr/bin/qtpath && chmod +x /usr/bin/qtpath +RUN echo '#!/bin/bash\nsource qtpath\nwine qtdeploy "$@"' > /usr/bin/qtdeploy && chmod +x /usr/bin/qtdeploy +RUN echo '#!/bin/bash\nsource qtpath\nwine qtminimal "$@"' > /usr/bin/qtminimal && chmod +x /usr/bin/qtminimal +RUN echo '#!/bin/bash\nsource qtpath\nwine qtmoc "$@"' > /usr/bin/qtmoc && chmod +x /usr/bin/qtmoc +RUN echo '#!/bin/bash\nsource qtpath\nwine qtrcc "$@"' > /usr/bin/qtrcc && chmod +x /usr/bin/qtrcc +RUN ln -s $HOME/.wine/drive_c/gopath $HOME/work + diff --git a/qt/tool-chain/docker/wine/Dockerfile.base b/qt/tool-chain/docker/wine/Dockerfile.base new file mode 100644 index 0000000..b4cdc6e --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.base @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/.wine/drive_c/gopath +ENV WINEDEBUG -all + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git && apt-get -qq clean +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local && rm -f $GO +RUN GOOS=windows /usr/local/go/bin/go get -d -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging xvfb cabextract && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" xvfb-run wineboot && wineserver -w +RUN curl -sL --retry 10 --retry-delay 60 -O https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks && chmod +x winetricks && mv winetricks /usr/bin && xvfb-run winetricks -q vcrun2015 && rm -f winetricks + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install unzip && apt-get -qq clean +RUN GO=go1.11.2.windows-amd64.zip && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && unzip -qq $GO -d $HOME/.wine/drive_c && rm -f $GO +RUN GOPATH=C:\\gopath wine C:\\go\\bin\\go install -ldflags="-s -w" -tags=no_env github.com/peterq/pan-light/qt/cmd/... diff --git a/qt/tool-chain/docker/wine/Dockerfile.base_xp b/qt/tool-chain/docker/wine/Dockerfile.base_xp new file mode 100644 index 0000000..35c99e2 --- /dev/null +++ b/qt/tool-chain/docker/wine/Dockerfile.base_xp @@ -0,0 +1,24 @@ +FROM ubuntu:16.04 +LABEL maintainer therecipe + +ENV USER user +ENV HOME /home/$USER +ENV GOPATH $HOME/.wine/drive_c/gopath +ENV WINEDEBUG -all + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git && apt-get -qq clean +RUN GO=go1.11.2.linux-amd64.tar.gz && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && tar -xzf $GO -C /usr/local && rm -f $GO +RUN GOOS=windows /usr/local/go/bin/go get -d -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install software-properties-common apt-transport-https && apt-get -qq clean +RUN dpkg --add-architecture i386 +RUN curl -sL --retry 10 --retry-delay 60 https://dl.winehq.org/wine-builds/winehq.key | apt-key add - +RUN apt-add-repository https://dl.winehq.org/wine-builds/ubuntu/ +RUN apt-add-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ xenial main' +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install winehq-staging xvfb cabextract && apt-get -qq clean +RUN WINEDLLOVERRIDES="mscoree,mshtml=" xvfb-run wineboot && wineserver -w +RUN curl -sL --retry 10 --retry-delay 60 -O https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks && chmod +x winetricks && mv winetricks /usr/bin && xvfb-run winetricks -q vcrun2015 && rm -f winetricks + +RUN apt-get -qq update && apt-get --no-install-recommends -qq -y install unzip && apt-get -qq clean +RUN GO=go1.10.5.windows-amd64.zip && curl -sL --retry 10 --retry-delay 60 -O https://dl.google.com/go/$GO && unzip -qq $GO -d $HOME/.wine/drive_c && rm -f $GO +RUN GOPATH=C:\\gopath wine C:\\go\\bin\\go install -tags=no_env github.com/peterq/pan-light/qt/cmd/... diff --git a/qt/tool-chain/docker/wine/rspfile.patch b/qt/tool-chain/docker/wine/rspfile.patch new file mode 100644 index 0000000..c2863dc --- /dev/null +++ b/qt/tool-chain/docker/wine/rspfile.patch @@ -0,0 +1,133 @@ +diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go +index 158f5f3b17..53f7c38a09 100644 +--- a/src/cmd/go/internal/work/exec.go ++++ b/src/cmd/go/internal/work/exec.go +@@ -2822,7 +2822,11 @@ func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) { + cleanup = func() { os.Remove(tf.Name()) } + var buf bytes.Buffer + for _, arg := range cmd.Args[1:] { +- fmt.Fprintf(&buf, "%s\n", arg) ++ if strings.Contains(arg, "\\") { ++ fmt.Fprintf(&buf, "%s\n", strings.Replace(arg, "\\", "\\\\", -1)) ++ } else { ++ fmt.Fprintf(&buf, "%s\n", arg) ++ } + } + if _, err := tf.Write(buf.Bytes()); err != nil { + tf.Close() +@@ -2848,7 +2852,7 @@ func useResponseFile(path string, argLen int) bool { + // TODO: do we need more commands? asm? cgo? For now, no. + prog := strings.TrimSuffix(filepath.Base(path), ".exe") + switch prog { +- case "compile", "link": ++ case "compile", "link", "gcc", "g++": + default: + return false + } +diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go +index 2284c347dc..c94ce4bef2 100644 +--- a/src/cmd/link/internal/ld/lib.go ++++ b/src/cmd/link/internal/ld/lib.go +@@ -50,6 +50,7 @@ import ( + "io" + "io/ioutil" + "log" ++ "math/rand" + "os" + "os/exec" + "path/filepath" +@@ -1337,7 +1338,10 @@ func (ctxt *Link) hostlink() { + ctxt.Logf("\n") + } + +- if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil { ++ cmd := exec.Command(argv[0], argv[1:]...) ++ cleanup := passLongArgsInResponseFiles(cmd) ++ defer cleanup() ++ if out, err := cmd.CombinedOutput(); err != nil { + Exitf("running %s failed: %v\n%s", argv[0], err, out) + } else if len(out) > 0 { + // always print external output even if the command is successful, so that we don't +@@ -2366,3 +2370,82 @@ func dfs(lib *sym.Library, mark map[*sym.Library]markKind, order *[]*sym.Library + mark[lib] = visited + *order = append(*order, lib) + } ++ ++// passLongArgsInResponseFiles modifies cmd on Windows such that, for ++// certain programs, long arguments are passed in "response files", a ++// file on disk with the arguments, with one arg per line. An actual ++// argument starting with '@' means that the rest of the argument is ++// a filename of arguments to expand. ++// ++// See Issue 18468. ++func passLongArgsInResponseFiles(cmd *exec.Cmd) (cleanup func()) { ++ cleanup = func() {} // no cleanup by default ++ ++ var argLen int ++ for _, arg := range cmd.Args { ++ argLen += len(arg) ++ } ++ ++ // If we're not approaching 32KB of args, just pass args normally. ++ // (use 30KB instead to be conservative; not sure how accounting is done) ++ if !useResponseFile(cmd.Path, argLen) { ++ return ++ } ++ ++ tf, err := ioutil.TempFile("", "args") ++ if err != nil { ++ log.Fatalf("error writing long arguments to response file: %v", err) ++ } ++ cleanup = func() { os.Remove(tf.Name()) } ++ var buf bytes.Buffer ++ for _, arg := range cmd.Args[1:] { ++ if strings.Contains(arg, "\\") { ++ fmt.Fprintf(&buf, "%s\n", strings.Replace(arg, "\\", "\\\\", -1)) ++ } else { ++ fmt.Fprintf(&buf, "%s\n", arg) ++ } ++ } ++ if _, err := tf.Write(buf.Bytes()); err != nil { ++ tf.Close() ++ cleanup() ++ log.Fatalf("error writing long arguments to response file: %v", err) ++ } ++ if err := tf.Close(); err != nil { ++ cleanup() ++ log.Fatalf("error writing long arguments to response file: %v", err) ++ } ++ cmd.Args = []string{cmd.Args[0], "@" + tf.Name()} ++ return cleanup ++} ++ ++func useResponseFile(path string, argLen int) bool { ++ // Unless we're on Windows, don't use response files. ++ if runtime.GOOS != "windows" { ++ return false ++ } ++ ++ // Unless the program uses objabi.Flagparse, which understands ++ // response files, don't use response files. ++ // TODO: do we need more commands? asm? cgo? For now, no. ++ prog := strings.TrimSuffix(filepath.Base(path), ".exe") ++ switch prog { ++ case "gcc", "g++": ++ default: ++ return false ++ } ++ ++ // Windows has a limit of 32 KB arguments. To be conservative and not ++ // worry about whether that includes spaces or not, just use 30 KB. ++ if argLen > (30 << 10) { ++ return true ++ } ++ ++ // On the Go build system, use response files about 10% of the ++ // time, just to exercise this codepath. ++ isBuilder := os.Getenv("GO_BUILDER_NAME") != "" ++ if isBuilder && rand.Intn(10) == 0 { ++ return true ++ } ++ ++ return false ++} diff --git a/qt/tool-chain/utils/android.go b/qt/tool-chain/utils/android.go new file mode 100644 index 0000000..3bd539f --- /dev/null +++ b/qt/tool-chain/utils/android.go @@ -0,0 +1,58 @@ +package utils + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +//TODO: support for JDK 9 +func JDK_DIR() string { + if dir, ok := os.LookupEnv("JDK_DIR"); ok { + return filepath.Clean(dir) + } + if dir, ok := os.LookupEnv("JAVA_HOME"); ok { + return filepath.Clean(dir) + } + switch runtime.GOOS { + case "windows": + return fmt.Sprintf(windowsSystemDrive()+"\\Program Files\\Java\\jdk%v", strings.Split(RunCmd(exec.Command("java", "-version"), "deploy.jdk"), "\"")[1]) + case "darwin": + return fmt.Sprintf("/Library/Java/JavaVirtualMachines/jdk%v.jdk/Contents/Home", strings.Split(RunCmd(exec.Command("java", "-version"), "deploy.jdk"), "\"")[1]) + default: + return filepath.Join(os.Getenv("HOME"), "jdk") + } +} + +func ANDROID_SDK_DIR() string { + if dir, ok := os.LookupEnv("ANDROID_SDK_DIR"); ok { + return filepath.Clean(dir) + } + if dir, ok := os.LookupEnv("ANDROID_SDK_ROOT"); ok { + return filepath.Clean(dir) + } + switch runtime.GOOS { + case "windows": + return windowsSystemDrive() + "\\android-sdk-windows" + case "darwin": + return filepath.Join(os.Getenv("HOME"), "android-sdk-macosx") + default: + return filepath.Join(os.Getenv("HOME"), "android-sdk-linux") + } +} + +func ANDROID_NDK_DIR() string { + if dir, ok := os.LookupEnv("ANDROID_NDK_DIR"); ok { + return filepath.Clean(dir) + } + if dir, ok := os.LookupEnv("ANDROID_NDK_ROOT"); ok { + return filepath.Clean(dir) + } + if runtime.GOOS == "windows" { + return windowsSystemDrive() + "\\android-ndk-r18b" + } + return filepath.Join(os.Getenv("HOME"), "android-ndk-r18b") +} diff --git a/qt/tool-chain/utils/darwin.go b/qt/tool-chain/utils/darwin.go new file mode 100644 index 0000000..ede32f2 --- /dev/null +++ b/qt/tool-chain/utils/darwin.go @@ -0,0 +1,150 @@ +package utils + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" +) + +var sdkMutex = new(sync.Mutex) + +func XCODE_DIR() string { + if dir, ok := os.LookupEnv("XCODE_DIR"); ok { + return filepath.Clean(dir) + } + return filepath.Join("/Applications/Xcode.app") +} + +var _MACOS_SDK_DIR string + +func MACOS_SDK_DIR() string { + sdkMutex.Lock() + defer sdkMutex.Unlock() + if _MACOS_SDK_DIR != "" { + return _MACOS_SDK_DIR + } + if runtime.GOOS == "darwin" { + basePath := filepath.Join(XCODE_DIR(), "Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs") + for maj := 10; maj < 50; maj++ { + for min := 0; min < 25; min++ { + i := fmt.Sprintf("%v.%v", maj, min) + if ExistsDir(filepath.Join(basePath, fmt.Sprintf("MacOSX%v.sdk", i))) { + _MACOS_SDK_DIR = fmt.Sprintf("MacOSX%v.sdk", i) + return _MACOS_SDK_DIR + } + } + } + if ExistsDir(filepath.Join(basePath, "MacOSX.sdk")) { + _MACOS_SDK_DIR = "MacOSX.sdk" + return _MACOS_SDK_DIR + } + Log.Errorf("failed to find MacOSX sdk in %v", basePath) + } + return "" +} + +var _IPHONEOS_SDK_DIR string + +func IPHONEOS_SDK_DIR() string { + sdkMutex.Lock() + defer sdkMutex.Unlock() + if _IPHONEOS_SDK_DIR != "" { + return _IPHONEOS_SDK_DIR + } + if runtime.GOOS == "darwin" { + basePath := filepath.Join(XCODE_DIR(), "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs") + for maj := 10; maj < 50; maj++ { + for min := 0; min < 25; min++ { + i := fmt.Sprintf("%v.%v", maj, min) + if ExistsDir(filepath.Join(basePath, fmt.Sprintf("iPhoneOS%v.sdk", i))) { + _IPHONEOS_SDK_DIR = fmt.Sprintf("iPhoneOS%v.sdk", i) + return _IPHONEOS_SDK_DIR + } + } + } + if ExistsDir(filepath.Join(basePath, "iPhoneOS.sdk")) { + _IPHONEOS_SDK_DIR = "iPhoneOS.sdk" + return _IPHONEOS_SDK_DIR + } + Log.Errorf("failed to find iPhoneOS sdk in %v", basePath) + } + return "" +} + +var _IPHONESIMULATOR_SDK_DIR string + +func IPHONESIMULATOR_SDK_DIR() string { + sdkMutex.Lock() + defer sdkMutex.Unlock() + if _IPHONESIMULATOR_SDK_DIR != "" { + return _IPHONESIMULATOR_SDK_DIR + } + if runtime.GOOS == "darwin" { + basePath := filepath.Join(XCODE_DIR(), "Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs") + for maj := 10; maj < 50; maj++ { + for min := 0; min < 25; min++ { + i := fmt.Sprintf("%v.%v", maj, min) + if ExistsDir(filepath.Join(basePath, fmt.Sprintf("iPhoneSimulator%v.sdk", i))) { + _IPHONESIMULATOR_SDK_DIR = fmt.Sprintf("iPhoneSimulator%v.sdk", i) + return _IPHONESIMULATOR_SDK_DIR + } + } + } + if ExistsDir(filepath.Join(basePath, "iPhoneSimulator.sdk")) { + _IPHONESIMULATOR_SDK_DIR = "iPhoneSimulator.sdk" + return _IPHONESIMULATOR_SDK_DIR + } + Log.Errorf("failed to find iPhoneSimulator sdk in %v", basePath) + } + return "" +} + +func QT_HOMEBREW() bool { + return os.Getenv("QT_HOMEBREW") == "true" || isHomeBrewQtDir() +} + +func QT_MACPORTS() bool { + return os.Getenv("QT_MACPORTS") == "true" +} + +func QT_NIX() bool { + _, ok := os.LookupEnv("NIX_STORE") + return ok +} + +func isHomeBrewQtDir() bool { + return ExistsFile(filepath.Join(QT_DIR(), "INSTALL_RECEIPT.json")) +} + +func QT_DARWIN_DIR() string { + path := qT_DARWIN_DIR() + if ExistsDir(path) { + return path + } + return strings.Replace(path, QT_VERSION_MAJOR(), QT_VERSION(), -1) +} + +var qt_darwin_dir_nix string + +func qT_DARWIN_DIR() string { + if QT_HOMEBREW() { + if isHomeBrewQtDir() { + return QT_DIR() + } + return "/usr/local/opt/qt5" + } + if QT_MACPORTS() { + return "/opt/local/libexec/qt5" + } + if QT_NIX() { + if len(qt_darwin_dir_nix) == 0 { + qt_darwin_dir_nix = strings.TrimSpace(RunCmd(exec.Command(ToolPath("qmake", "darwin"), "-query", "QT_INSTALL_PREFIX"), "nix qt dir")) + } + return qt_darwin_dir_nix + } + return filepath.Join(QT_DIR(), fmt.Sprintf("%v/clang_64", QT_VERSION_MAJOR())) +} diff --git a/qt/tool-chain/utils/env.go b/qt/tool-chain/utils/env.go new file mode 100644 index 0000000..7542b3e --- /dev/null +++ b/qt/tool-chain/utils/env.go @@ -0,0 +1,307 @@ +package utils + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" +) + +var qT_VERSION_CACHE string + +func QT_VERSION() string { + if version, ok := os.LookupEnv("QT_VERSION"); ok { + return version + } + if QT_PKG_CONFIG() { + if qT_VERSION_CACHE == "" { + qT_VERSION_CACHE = strings.TrimSpace(RunCmd(exec.Command("pkg-config", "--modversion", "Qt5Core"), "cgo.LinuxPkgConfig_modVersion")) + } + return qT_VERSION_CACHE + } + return "5.12.0" +} + +func QT_VERSION_NUM() int { + version := QT_VERSION() + vmaj, _ := strconv.Atoi(string(version[0])) + vmin, _ := strconv.Atoi(strings.Replace(version[1:], ".", "", -1)) + return vmaj*1e3 + vmin +} + +func QT_VERSION_MAJOR() string { + if version, ok := os.LookupEnv("QT_VERSION_MAJOR"); ok { + return version + } + if QT_VERSION_NUM() >= 5091 { + return QT_VERSION() + } + return strings.Join(strings.Split(QT_VERSION(), ".")[:2], ".") +} + +func QT_API(def string) string { + if api, ok := os.LookupEnv("QT_API"); ok { + return api + } + return def +} + +func QT_API_NUM(def string) int { + version := QT_API(def) + vmaj, _ := strconv.Atoi(string(version[0])) + vmin, _ := strconv.Atoi(strings.Replace(version[1:], ".", "", -1)) + return vmaj*1e3 + vmin +} + +func QT_DIR() string { + path := qT_DIR() + if ExistsDir(path) { + return path + } + return strings.Replace(path, QT_VERSION_MAJOR(), QT_VERSION(), -1) +} + +func qT_DIR() string { + if dir, ok := os.LookupEnv("QT_DIR"); ok { + return filepath.Clean(dir) + } + + prefix := os.Getenv("HOME") + if runtime.GOOS == "windows" { + prefix = windowsSystemDrive() + "\\" + } + + if dir := filepath.Join(prefix, "Qt", "Qt"+QT_VERSION()); ExistsDir(dir) { + return dir + } + + if dir := filepath.Join(prefix, "Qt"+QT_VERSION()); ExistsDir(dir) { + return dir + } + + return filepath.Join(prefix, "Qt") +} + +func QT_FAT() bool { + return os.Getenv("QT_FAT") == "true" +} + +func QT_STUB() bool { + return os.Getenv("QT_STUB") == "true" && !QT_FAT() +} + +func QT_DEBUG() bool { + return os.Getenv("QT_DEBUG") == "true" +} + +func QT_DEBUG_QML() bool { + return os.Getenv("QT_DEBUG_QML") == "true" +} + +func QT_DEBUG_CONSOLE() bool { + return os.Getenv("QT_DEBUG_CONSOLE") == "true" +} + +func CheckBuildTarget(buildTarget string) { + switch buildTarget { + case "android", "android-emulator", + "ios", "ios-simulator", + "sailfish", "sailfish-emulator", "asteroid", + "rpi1", "rpi2", "rpi3", + "windows", "darwin", "linux", + "homebrew", "ubports", + "js", "wasm": //TODO: pkg_config ? + default: + if !strings.Contains(buildTarget, "_") { + Log.Panicf("failed to recognize build target %v", buildTarget) + } + } + if buildTarget != runtime.GOOS && !strings.Contains(buildTarget, "_") { + switch { + case QT_MSYS2(): + Log.Fatalf("%v is not supported as a deploy target on %v with MSYS2 -> install the official Qt version instead and try again", buildTarget, runtime.GOOS) + case QT_HOMEBREW(), QT_MACPORTS(), QT_NIX(): + Log.Fatalf("%v is not supported as a deploy target on %v with HomeBrew/MacPorts/Nix -> install the official Qt version instead and try again", buildTarget, runtime.GOOS) + case QT_PKG_CONFIG() && !QT_UBPORTS(): + Log.Fatalf("%v is not supported as a deploy target on %v with PkgConfig -> install the official Qt version instead and try again", buildTarget, runtime.GOOS) + } + } +} + +func CI() bool { + return os.Getenv("CI") == "true" +} + +func QT_QMAKE_DIR() string { + if dir, ok := os.LookupEnv("QT_QMAKE_DIR"); ok { + return filepath.Clean(dir) + } + return "" +} + +func QT_DOCKER() bool { + return os.Getenv("QT_DOCKER") == "true" +} + +func QT_VAGRANT() bool { + return os.Getenv("QT_VAGRANT") == "true" +} + +//TODO: use qmake props +func ToolPath(tool, target string) string { + if dir := QT_QMAKE_DIR(); dir != "" { + return filepath.Join(dir, tool) + } + + if strings.HasPrefix(target, "sailfish") && !QT_SAILFISH() { + target = runtime.GOOS + } + + switch target { + case "darwin": + if QT_NIX() { + path, _ := exec.LookPath(tool) + path, _ = filepath.Abs(path) + return path + } + return filepath.Join(QT_DARWIN_DIR(), "bin", tool) + case "windows": + if runtime.GOOS == target { + if QT_MSYS2() { + if QT_MSYS2_STATIC() { + return filepath.Join(QT_MSYS2_DIR(), "qt5-static", "bin", tool) + } + return filepath.Join(QT_MSYS2_DIR(), "bin", tool) + } + path := filepath.Join(QT_DIR(), QT_VERSION_MAJOR(), "mingw73_64", "bin", tool) + if !ExistsDir(filepath.Join(QT_DIR(), QT_VERSION_MAJOR())) { + path = filepath.Join(QT_DIR(), QT_VERSION(), "mingw73_64", "bin", tool) + } + if !ExistsFile(path + ".exe") { + path = strings.Replace(path, "mingw73_64", "mingw53_32", -1) + } + if !ExistsFile(path + ".exe") { + path = strings.Replace(path, "mingw53_32", "mingw49_32", -1) + } + return path + } + return filepath.Join(QT_MXE_DIR(), "usr", QT_MXE_TRIPLET(), "qt5", "bin", tool) + case "linux", "ubports": + if QT_PKG_CONFIG() { + return filepath.Join(strings.TrimSpace(RunCmd(exec.Command("pkg-config", "--variable=host_bins", "Qt5Core"), "cgo.LinuxPkgConfig_hostBins")), tool) + } + path := filepath.Join(QT_DIR(), QT_VERSION_MAJOR(), "gcc_64", "bin", tool) + if !ExistsDir(filepath.Join(QT_DIR(), QT_VERSION_MAJOR())) { + path = filepath.Join(QT_DIR(), QT_VERSION(), "gcc_64", "bin", tool) + } + return path + case "ios", "ios-simulator": + return filepath.Join(QT_DIR(), QT_VERSION_MAJOR(), "ios", "bin", tool) + case "android": + return filepath.Join(QT_DIR(), QT_VERSION_MAJOR(), "android_armv7", "bin", tool) + case "android-emulator": + return filepath.Join(QT_DIR(), QT_VERSION_MAJOR(), "android_x86", "bin", tool) + case "sailfish", "sailfish-emulator": + return filepath.Join("/srv/mer/targets/SailfishOS-"+QT_SAILFISH_VERSION()+"-i486/usr/lib/qt5/bin/", tool) + //TODO support indirect access on desktop: return filepath.Join(os.Getenv("HOME"), ".config", "SailfishOS-SDK", "mer-sdk-tools", "MerSDK", "SailfishOS-i486", tool) + case "asteroid": + //TODO: + case "rp1", "rpi2", "rpi3": + return filepath.Join(QT_DIR(), QT_VERSION_MAJOR(), target, "bin", tool) + } + return "" +} + +//TODO: detect webkit support automatically +func QT_WEBKIT() bool { + return os.Getenv("QT_WEBKIT") == "true" +} + +func CGO_CFLAGS_ALLOW() string { + if allowed, ok := os.LookupEnv("CGO_CFLAGS_ALLOW"); ok { + return allowed + } + return ".*" +} + +func CGO_CXXFLAGS_ALLOW() string { + if allowed, ok := os.LookupEnv("CGO_CXXFLAGS_ALLOW"); ok { + return allowed + } + return ".*" +} + +func CGO_LDFLAGS_ALLOW() string { + if allowed, ok := os.LookupEnv("CGO_LDFLAGS_ALLOW"); ok { + return allowed + } + return ".*" +} + +func GOARCH() string { + if arch, ok := os.LookupEnv("GOARCH"); ok { + return arch + } + return runtime.GOARCH +} + +func QT_DYNAMIC_SETUP() bool { + return os.Getenv("QT_DYNAMIC_SETUP") == "true" +} + +func GOFLAGS() string { + if flags, ok := os.LookupEnv("GOFLAGS"); ok { + return flags + } + if UseGOMOD("") { + return "-mod=vendor" + } + return "" +} + +func GOMOD(path string) string { + if mod, ok := os.LookupEnv("GOMOD"); ok { + return mod + } + cmd := exec.Command("go", "env", "GOMOD") + cmd.Dir = path + return strings.TrimSpace(RunCmd(cmd, "GOMOD")) +} + +var ( + useGOMOD bool + useGOMODMutex = new(sync.Mutex) +) + +func UseGOMOD(path string) (r bool) { + useGOMODMutex.Lock() + if !useGOMOD && len(GOMOD(path)) != 0 { + useGOMOD = true + } + r = useGOMOD + useGOMODMutex.Unlock() + return +} + +func QT_GEN_OPENGL() bool { + return os.Getenv("QT_GEN_OPENGL") == "true" +} + +func GoList(args ...string) *exec.Cmd { + cmd := exec.Command("go", "list") + if UseGOMOD("") { + if true || /*strings.Contains(strings.Join(args, "|"), "github.com/therecipe/env_"+runtime.GOOS+"_amd64") ||*/ strings.Contains(strings.Join(args, "|"), "github.com/peterq/pan-light/qt/tool-chain") { + //TODO: make env readonly if it can't be found inside ./vendor ... + cmd.Args = append(cmd.Args, "-mod=readonly") + } else { + cmd.Args = append(cmd.Args, GOFLAGS()) + } + } + cmd.Args = append(cmd.Args, "-e", "-f") + cmd.Args = append(cmd.Args, args...) + cmd.Env = append(os.Environ(), []string{"CGO_ENABLED=0"}...) + return cmd +} diff --git a/qt/tool-chain/utils/gopath.go b/qt/tool-chain/utils/gopath.go new file mode 100644 index 0000000..3c68813 --- /dev/null +++ b/qt/tool-chain/utils/gopath.go @@ -0,0 +1,53 @@ +package utils + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "sync" +) + +const packageName = "github.com/peterq/pan-light/qt/bindings" + +var mustGoPath string +var mustGoPathMutex = new(sync.Mutex) + +//GOBIN returns the general GOBIN string +func GOBIN() string { + if dir, ok := os.LookupEnv("GOBIN"); ok { + return filepath.Clean(dir) + } + return filepath.Join(MustGoPath(), "bin") +} + +// MustGoPath returns the GOPATH that holds this package +// it exits if any error occurres and also caches the result +func MustGoPath() string { + mustGoPathMutex.Lock() + if len(mustGoPath) == 0 { + mustGoPath = strings.TrimSpace(RunCmd(GoList("{{.Root}}", "github.com/peterq/pan-light/qt"), "get list gopath")) + if len(mustGoPath) == 0 { + mustGoPath = GOPATH() + } + } + mustGoPathMutex.Unlock() + return mustGoPath +} + +// GOPATH returns the general GOPATH string +func GOPATH() string { + if dir, ok := os.LookupEnv("GOPATH"); ok { + return dir + } + + home := "HOME" + if runtime.GOOS == "windows" { + home = "USERPROFILE" + } + if dir, ok := os.LookupEnv(home); ok { + return filepath.Join(dir, "go") + } + + return "" +} diff --git a/qt/tool-chain/utils/linux.go b/qt/tool-chain/utils/linux.go new file mode 100644 index 0000000..c436de5 --- /dev/null +++ b/qt/tool-chain/utils/linux.go @@ -0,0 +1,124 @@ +package utils + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func QT_PKG_CONFIG() bool { + return os.Getenv("QT_PKG_CONFIG") == "true" +} + +func QT_DOC_DIR() string { + if dir, ok := os.LookupEnv("QT_DOC_DIR"); ok { + return filepath.Clean(dir) + } + switch QT_DISTRO() { + case "arch": + return "/usr/share/doc/qt" + case "fedora": + return "/usr/share/doc/qt5" + case "suse": + return "/usr/share/doc/packages/qt5" + case "ubuntu": + return "/usr/share/qt5/doc" + case "gentoo": + return "/usr/share/doc/qt-" + QT_VERSION() + default: + Log.Error("failed to detect the Linux distro") + return "" + } +} + +func QT_MISC_DIR() string { + if dir, ok := os.LookupEnv("QT_MISC_DIR"); ok { + return filepath.Clean(dir) + } + if QT_DISTRO() == "arch" { + return filepath.Join(strings.TrimSpace(RunCmd(exec.Command("pkg-config", "--variable=libdir", "Qt5Core"), "cgo.LinuxPkgConfig_libDir")), "qt") + } + //fedora, suse, ubuntu, gentoo + return strings.TrimSuffix(strings.TrimSpace(RunCmd(exec.Command("pkg-config", "--variable=host_bins", "Qt5Core"), "cgo.LinuxPkgConfig_hostBins")), "/bin") +} + +func QT_DISTRO() string { + if distro, ok := os.LookupEnv("QT_DISTRO"); ok { + return distro + } + if _, err := exec.LookPath("pacman"); err == nil { + return "arch" + } + if _, err := exec.LookPath("yum"); err == nil { + return "fedora" + } + if _, err := exec.LookPath("zypper"); err == nil { + return "suse" + } + if _, err := exec.LookPath("apt-get"); err == nil { + return "ubuntu" + } + if _, err := exec.LookPath("emerge"); err == nil { + return "gentoo" + } + Log.Error("failed to detect the Linux distro") + return "" +} + +func QT_MXE_ARCH() string { + if arch := os.Getenv("QT_MXE_ARCH"); arch == "amd64" { + return arch + } + return "386" +} + +func QT_MXE_STATIC() bool { + return os.Getenv("QT_MXE_STATIC") == "true" +} + +func QT_MXE_TRIPLET() string { + prefix := "i686" + if QT_MXE_ARCH() == "amd64" { + prefix = "x86_64" + } + suffix := "shared" + if QT_MXE_STATIC() { + suffix = "static" + } + return fmt.Sprintf("%v-w64-mingw32.%v", prefix, suffix) +} + +func QT_MXE_DIR() string { + if dir, ok := os.LookupEnv("QT_MXE_DIR"); ok { + return filepath.Clean(dir) + } + return filepath.Join("/usr", "lib", "mxe") +} + +func QT_MXE_BIN(tool string) string { + return filepath.Join(QT_MXE_DIR(), "usr", "bin", fmt.Sprintf("%v-%v", QT_MXE_TRIPLET(), tool)) +} + +func QT_MXE() bool { + return os.Getenv("QT_MXE") == "true" +} + +func QT_UBPORTS() bool { + return os.Getenv("QT_UBPORTS") == "true" +} + +func QT_UBPORTS_ARCH() string { + if arch := os.Getenv("QT_UBPORTS_ARCH"); arch == "amd64" { + return arch + } + return "arm" +} + +func QT_UBPORTS_VERSION() string { + if rel := os.Getenv("QT_UBPORTS_VERSION"); rel == "xenial" { + return rel + } + return "vivid" +} diff --git a/qt/tool-chain/utils/logger.go b/qt/tool-chain/utils/logger.go new file mode 100644 index 0000000..9db8a3b --- /dev/null +++ b/qt/tool-chain/utils/logger.go @@ -0,0 +1,18 @@ +package utils + +import ( + "os" + + "github.com/sirupsen/logrus" +) + +var Log = logrus.New() + +func init() { + Log.Out = os.Stderr + Log.Level = logrus.InfoLevel +} + +func Debug() { + Log.Level = logrus.DebugLevel +} diff --git a/qt/tool-chain/utils/rpi.go b/qt/tool-chain/utils/rpi.go new file mode 100644 index 0000000..8470d79 --- /dev/null +++ b/qt/tool-chain/utils/rpi.go @@ -0,0 +1,45 @@ +package utils + +import ( + "os" + "path/filepath" +) + +func QT_RPI() bool { + return os.Getenv("QT_RPI") == "true" +} + +func RPI_COMPILER() string { + if name, ok := os.LookupEnv("RPI_COMPILER"); ok { + return name + } + return "arm-rpi-4.9.3-linux-gnueabihf" +} + +func RPI_TOOLS_DIR() string { + if dir, ok := os.LookupEnv("RPI_TOOLS_DIR"); ok { + return filepath.Clean(dir) + } + return filepath.Join(os.Getenv("HOME"), "raspi/tools") +} + +func RPI1_SYSROOT_DIR() string { + if dir, ok := os.LookupEnv("RPI1_SYSROOT_DIR"); ok { + return filepath.Clean(dir) + } + return filepath.Join(os.Getenv("HOME"), "raspi/sysroot") +} + +func RPI2_SYSROOT_DIR() string { + if dir, ok := os.LookupEnv("RPI2_SYSROOT_DIR"); ok { + return filepath.Clean(dir) + } + return filepath.Join(os.Getenv("HOME"), "raspi/sysroot") +} + +func RPI3_SYSROOT_DIR() string { + if dir, ok := os.LookupEnv("RPI3_SYSROOT_DIR"); ok { + return filepath.Clean(dir) + } + return filepath.Join(os.Getenv("HOME"), "raspi/sysroot") +} diff --git a/qt/tool-chain/utils/sailfish.go b/qt/tool-chain/utils/sailfish.go new file mode 100644 index 0000000..392bf23 --- /dev/null +++ b/qt/tool-chain/utils/sailfish.go @@ -0,0 +1,50 @@ +package utils + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" +) + +func VIRTUALBOX_DIR() string { + if dir, ok := os.LookupEnv("VIRTUALBOX_DIR"); ok { + return filepath.Clean(dir) + } + if runtime.GOOS == "windows" { + return windowsSystemDrive() + "\\Program Files\\Oracle\\VirtualBox" + } + path, err := exec.LookPath("vboxmanage") + if err != nil { + Log.WithError(err).Error("failed to find vboxmanage in your PATH") + } + path = filepath.Dir(path) + if !filepath.IsAbs(path) { + path, err = filepath.Abs(path) + if err != nil { + Log.WithError(err).WithField("path", path).Fatal("can't resolve absolute path") + } + } + return path +} + +func SAILFISH_DIR() string { + if dir, ok := os.LookupEnv("SAILFISH_DIR"); ok { + return filepath.Clean(dir) + } + if runtime.GOOS == "windows" { + return windowsSystemDrive() + "\\SailfishOS" + } + return filepath.Join(os.Getenv("HOME"), "SailfishOS") +} + +func QT_SAILFISH() bool { + return os.Getenv("QT_SAILFISH") == "true" +} + +func QT_SAILFISH_VERSION() string { + if ver, ok := os.LookupEnv("QT_SAILFISH_VERSION"); ok { + return ver + } + return "2.2.1.18" +} diff --git a/qt/tool-chain/utils/utils.go b/qt/tool-chain/utils/utils.go new file mode 100644 index 0000000..0b01420 --- /dev/null +++ b/qt/tool-chain/utils/utils.go @@ -0,0 +1,157 @@ +package utils + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/sirupsen/logrus" +) + +func ExistsFile(name string) bool { + _, err := ioutil.ReadFile(name) + return err == nil +} + +func ExistsDir(name string) bool { + _, err := ioutil.ReadDir(name) + return err == nil +} + +func MkdirAll(dir string) error { + err := os.MkdirAll(dir, 0755) + if err != nil { + Log.WithError(err).Panicf("failed to create dir %v", dir) + } + return err +} + +func RemoveAll(name string) error { + err := os.RemoveAll(name) + if err != nil { + Log.WithError(err).Panicf("failed to remove %v", name) + } + return err +} + +func Save(name, data string) error { + err := ioutil.WriteFile(name, []byte(data), 0644) + if err != nil { + Log.WithError(err).Panicf("failed to save %v", name) + } else { + Log.Debugf("saved file len(%v) %v", len(data), name) + } + return err +} + +func SaveExec(name, data string) error { + err := ioutil.WriteFile(name, []byte(data), 0755) + if err != nil { + Log.WithError(err).Panicf("failed to save %v", name) + } else { + Log.Debugf("saved file len(%v) %v", len(data), name) + } + return err +} + +func SaveBytes(name string, data []byte) error { + err := ioutil.WriteFile(name, data, 0644) + if err != nil { + Log.WithError(err).Panicf("failed to save %v", name) + } + return err +} + +//TODO: export error +func Load(name string) string { + out, err := ioutil.ReadFile(name) + if err != nil { + Log.WithError(err).Errorf("failed to load %v", name) + } + return string(out) +} + +//TODO: export error +func LoadOptional(name string) string { + out, err := ioutil.ReadFile(name) + if err != nil { + Log.WithError(err).Debugf("failed to load (optional) %v", name) + } + return string(out) +} + +var ( + goQtPkgPath string + goQtPkgPathMutex = new(sync.Mutex) +) + +func GoQtPkgPath(s ...string) (r string) { + goQtPkgPathMutex.Lock() + if len(goQtPkgPath) == 0 { + goQtPkgPath = strings.TrimSpace(RunCmd(GoList("{{.Dir}}", packageName), "utils.GoQtPkgPath")) + fmt.Println(goQtPkgPath) + os.Exit(0) + } + r = goQtPkgPath + goQtPkgPathMutex.Unlock() + return filepath.Join(r, filepath.Join(s...)) +} + +//TODO: export error +func RunCmd(cmd *exec.Cmd, name string) string { + fields := logrus.Fields{"_func": "RunCmd", "name": name, "cmd": strings.Join(cmd.Args, " "), "env": strings.Join(cmd.Env, " "), "dir": cmd.Dir} + Log.WithFields(fields).Debug("Execute") + out, err := runCmdHelper(cmd) + if err != nil { + Log.WithError(err).WithFields(fields).Error("failed to run command") + println(string(out)) + os.Exit(1) + } + return string(out) +} + +//TODO: export error +func RunCmdOptional(cmd *exec.Cmd, name string) string { + fields := logrus.Fields{"_func": "RunCmdOptional", "name": name, "cmd": strings.Join(cmd.Args, " "), "env": strings.Join(cmd.Env, " "), "dir": cmd.Dir} + Log.WithFields(fields).Debug("Execute") + out, err := runCmdHelper(cmd) + if err != nil && !strings.Contains(string(out), "No template (-t) specified") { + Log.WithError(err).WithFields(fields).Debug("failed to run command") + if Log.Level == logrus.DebugLevel { + println(string(out)) + } + } + return string(out) +} + +func RunCmdOptionalError(cmd *exec.Cmd, name string) (string, error) { + fields := logrus.Fields{"_func": "RunCmdOptionalError", "name": name, "cmd": strings.Join(cmd.Args, " "), "env": strings.Join(cmd.Env, " "), "dir": cmd.Dir} + Log.WithFields(fields).Debug("Execute") + out, err := runCmdHelper(cmd) + if err != nil { + Log.WithError(err).WithFields(fields).Debug("failed to run command") + if Log.Level == logrus.DebugLevel { + println(string(out)) + } + } + return string(out), err +} + +func runCmdHelper(cmd *exec.Cmd) (out []byte, err error) { + if _, ok := os.LookupEnv("WINEDEBUG"); ok { + go func() { out, err = cmd.CombinedOutput() }() + for range time.NewTicker(250 * time.Millisecond).C { + if cmd.ProcessState != nil && cmd.ProcessState.Exited() { + break + } + } + return + } + return cmd.Output() + //return cmd.CombinedOutput() +} diff --git a/qt/tool-chain/utils/walk.go b/qt/tool-chain/utils/walk.go new file mode 100644 index 0000000..25846b8 --- /dev/null +++ b/qt/tool-chain/utils/walk.go @@ -0,0 +1,115 @@ +package utils + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +// files that must be ignored +var blacklist = map[string]bool{ + ".git": true, + "android": true, + "deploy": true, + "ios": true, + "ios-simulator": true, + "node_modules": true, + "qml": true, + "rpi1": true, + "rpi2": true, + "rpi3": true, + "sailfish": true, + "sailfish-emulator": true, + "vendor": true, +} + +// WalkFilterBlacklist filter out blacklisted file +func WalkFilterBlacklist(root string, f filepath.WalkFunc) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + relPath, relErr := filepath.Rel(root, path) + if relErr != nil { + return relErr + } + if blacklist[relPath] { + // as the directory is blacklisted, just ignore everything under it + if info.IsDir() { + return filepath.SkipDir + } + return nil + } + return f(path, info, err) + } +} + +// WalkFilterDirectory only allow directory +func WalkOnlyDirectory(f filepath.WalkFunc) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return f(path, info, err) + } + return nil + } +} + +// WalkOnlyFile is opposite of WalkFilterDirectory, it only allow file +func WalkOnlyFile(f filepath.WalkFunc) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + return f(path, info, err) + } + return nil + } +} + +// WalkFilterPrefix only process file that do not have specified prefix +func WalkFilterPrefix(f filepath.WalkFunc, prefixes ...string) filepath.WalkFunc { + return func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + name := info.Name() + for index := range prefixes { + if strings.HasPrefix(name, prefixes[index]) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } + } + return f(path, info, err) + } +} + +// WalkOnlyExtension only process file (not directory) that their extension is +// specified, without the `.` such as `go` or `html`. +func WalkOnlyExtension(f filepath.WalkFunc, extensions ...string) filepath.WalkFunc { + compiled := make([]string, len(extensions)) + for index := range extensions { + compiled[index] = fmt.Sprintf(".%s", extensions[index]) + } + return func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return f(path, info, err) + } + extension := filepath.Ext(info.Name()) + for index := range compiled { + if extension == compiled[index] { + return f(path, info, err) + } + } + return nil + } +} diff --git a/qt/tool-chain/utils/walk_test.go b/qt/tool-chain/utils/walk_test.go new file mode 100644 index 0000000..1e47511 --- /dev/null +++ b/qt/tool-chain/utils/walk_test.go @@ -0,0 +1,155 @@ +package utils + +import ( + "io/ioutil" + "os" + "path/filepath" + "sort" + "testing" + + "github.com/sirupsen/logrus" + assert "github.com/stretchr/testify/require" +) + +var dummyData = []byte{0, 1, 2} + +func init() { + logrus.SetLevel(logrus.DebugLevel) + Log.Level = logrus.DebugLevel +} + +type walkResult struct { + output []string + root string +} + +func (w *walkResult) accumulate(path string, info os.FileInfo, err error) error { + if err == nil { + relPath, relErr := filepath.Rel(w.root, path) + if relErr != nil { + return relErr + } + w.output = append(w.output, relPath) + } + return err +} + +func (w *walkResult) sorted() []string { + output := w.output + sort.Strings(output) + return output +} + +func newWalkResult(root string) *walkResult { + return &walkResult{root: root} +} + +func mktemp(t *testing.T) string { + tempDir, err := ioutil.TempDir("", "walk_test") + assert.NoError(t, err) + assert.NotEmpty(t, tempDir) + return tempDir +} + +func TestWalkFilterBlacklist(t *testing.T) { + tempDir := mktemp(t) + defer func() { + _ = os.RemoveAll(tempDir) + }() + + blackListedFilename := filepath.Join(tempDir, "ios") + assert.NoError(t, ioutil.WriteFile(blackListedFilename, dummyData, 0644)) + + blackListedDir := filepath.Join(tempDir, ".git") + assert.NoError(t, os.Mkdir(blackListedDir, 0755)) + blackListedSubFilename := filepath.Join(blackListedDir, "config") + assert.NoError(t, ioutil.WriteFile(blackListedSubFilename, dummyData, 0644)) + + whiteListedFilename := filepath.Join(tempDir, "whiteListedFile.dat") + assert.NoError(t, ioutil.WriteFile(whiteListedFilename, dummyData, 0644)) + + whiteListedDirectory := filepath.Join(tempDir, "whiteListedDir") + assert.NoError(t, os.Mkdir(whiteListedDirectory, 0755)) + whiteListedSubFilename := filepath.Join(whiteListedDirectory, "whiteListedSubFilename") + assert.NoError(t, ioutil.WriteFile(whiteListedSubFilename, dummyData, 0644)) + + result := newWalkResult(tempDir) + assert.NoError(t, filepath.Walk(tempDir, WalkFilterBlacklist(tempDir, result.accumulate))) + output := result.sorted() + assert.Len(t, output, 4) + assert.Equal(t, ".", output[0]) + assert.Equal(t, "whiteListedDir", output[1]) + assert.Equal(t, "whiteListedDir/whiteListedSubFilename", output[2]) + assert.Equal(t, "whiteListedFile.dat", output[3]) +} + +func createSimpleFilesystem(tempDir string, t *testing.T) { + file := filepath.Join(tempDir, "file.txt") + assert.NoError(t, ioutil.WriteFile(file, dummyData, 0644)) + + dir := filepath.Join(tempDir, "dir.dirext") + assert.NoError(t, os.Mkdir(dir, 0755)) + subFile := filepath.Join(dir, "subfile.png") + assert.NoError(t, ioutil.WriteFile(subFile, dummyData, 0644)) +} + +func TestWalkOnlyDirectory(t *testing.T) { + tempDir := mktemp(t) + defer func() { + _ = os.RemoveAll(tempDir) + }() + createSimpleFilesystem(tempDir, t) + + result := newWalkResult(tempDir) + assert.NoError(t, filepath.Walk(tempDir, WalkOnlyDirectory(result.accumulate))) + output := result.sorted() + assert.Len(t, output, 2) + assert.Equal(t, ".", output[0]) + assert.Equal(t, "dir.dirext", output[1]) +} + +func TestWalkOnlyFile(t *testing.T) { + tempDir := mktemp(t) + defer func() { + _ = os.RemoveAll(tempDir) + }() + createSimpleFilesystem(tempDir, t) + + result := newWalkResult(tempDir) + assert.NoError(t, filepath.Walk(tempDir, WalkOnlyFile(result.accumulate))) + output := result.sorted() + assert.Len(t, output, 2) + assert.Equal(t, "dir.dirext/subfile.png", output[0]) + assert.Equal(t, "file.txt", output[1]) +} + +func TestWalkFilterPrefix(t *testing.T) { + tempDir := mktemp(t) + defer func() { + _ = os.RemoveAll(tempDir) + }() + createSimpleFilesystem(tempDir, t) + + result := newWalkResult(tempDir) + assert.NoError(t, filepath.Walk(tempDir, WalkFilterPrefix(result.accumulate, "dir"))) + output := result.sorted() + assert.Len(t, output, 2) + assert.Equal(t, ".", output[0]) + assert.Equal(t, "file.txt", output[1]) +} + +func TestWalkOnlyExtension(t *testing.T) { + tempDir := mktemp(t) + defer func() { + _ = os.RemoveAll(tempDir) + }() + createSimpleFilesystem(tempDir, t) + + result := newWalkResult(tempDir) + assert.NoError(t, filepath.Walk(tempDir, WalkOnlyExtension(result.accumulate, "txt"))) + output := result.sorted() + assert.Len(t, output, 3) + assert.Equal(t, ".", output[0]) + assert.Equal(t, "dir.dirext", output[1]) + assert.Equal(t, "file.txt", output[2]) +} diff --git a/qt/tool-chain/utils/windows.go b/qt/tool-chain/utils/windows.go new file mode 100644 index 0000000..ae9ecf6 --- /dev/null +++ b/qt/tool-chain/utils/windows.go @@ -0,0 +1,71 @@ +package utils + +import ( + "fmt" + "os" + "path/filepath" + "runtime" +) + +func QT_MSYS2() bool { + return (os.Getenv("QT_MSYS2") == "true" || IsMsys2QtDir() || MSYSTEM() != "") && !MSYS_DOCKER() +} + +func QT_MSYS2_DIR() string { + if dir, ok := os.LookupEnv("QT_MSYS2_DIR"); ok { + if QT_MSYS2_ARCH() == "amd64" { + return filepath.Join(dir, "mingw64") + } + return filepath.Join(dir, "mingw32") + } + prefix := "msys32" + if runtime.GOARCH == "amd64" { + prefix = "msys64" + } + suffix := "mingw32" + if QT_MSYS2_ARCH() == "amd64" { + suffix = "mingw64" + } + return fmt.Sprintf("%v\\%v\\%v", windowsSystemDrive(), prefix, suffix) +} + +func IsMsys2QtDir() bool { + return ExistsFile(filepath.Join(os.Getenv("QT_MSYS2_DIR"), "msys2.exe")) +} + +func QT_MSYS2_ARCH() string { + arch, ok := os.LookupEnv("QT_MSYS2_ARCH") + if ok { + return arch + } + if MSYSTEM() == "MINGW64" || (!ok && runtime.GOARCH == "amd64") { + return "amd64" + } + return "386" +} + +func QT_MSYS2_STATIC() bool { + return os.Getenv("QT_MSYS2_STATIC") == "true" +} + +func MSYSTEM() string { + return os.Getenv("MSYSTEM") +} + +func MSYS_DOCKER() bool { + _, ok := os.LookupEnv("DOCKER_MACHINE_NAME") + return ok +} + +func windowsSystemDrive() string { + if vol, ok := os.LookupEnv("SystemDrive"); ok { + return vol + } + if vol, ok := os.LookupEnv("SystemRoot"); ok { + return filepath.VolumeName(vol) + } + if vol, ok := os.LookupEnv("WinDir"); ok { + return filepath.VolumeName(vol) + } + return "C:" +} diff --git a/qt/tool-chain/vagrant/darwin/Vagrantfile b/qt/tool-chain/vagrant/darwin/Vagrantfile new file mode 100644 index 0000000..d38757f --- /dev/null +++ b/qt/tool-chain/vagrant/darwin/Vagrantfile @@ -0,0 +1,46 @@ +Vagrant.configure("2") do |config| + config.vm.box = "emil-appunite/macos10.13-xcode9.1" + config.vm.network :private_network, ip: "192.168.10.2" + config.vm.synced_folder ".", "/vagrant", type: "nfs", SharedFoldersEnableSymlinksCreate: false + config.vm.synced_folder ENV['GOPATH'], "/media/sf_GOPATH0", type: "nfs", SharedFoldersEnableSymlinksCreate: false + config.ssh.keep_alive = true + + config.vm.provider "virtualbox" do |v| + v.linked_clone = true + v.memory = 8192 + v.customize ["modifyvm", :id, "--vram", "256"] + v.cpus = 1 + v.customize ["modifyvm", :id, "--accelerate2dvideo", "on"] + end + + config.vm.define "darwin" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "darwin", "DESKTOP" => "true"} + m.vm.provision "shell", path: "../../ci/darwin.sh", privileged: false, env: {"OS" => "darwin", "DESKTOP" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "darwin", "DESKTOP" => "true"} + end + + config.vm.define "homebrew" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "darwin", "DESKTOP" => "true", "QT_HOMEBREW" => "true"} + m.vm.provision "shell", path: "../../ci/darwin.sh", privileged: false, env: {"OS" => "darwin", "DESKTOP" => "true", "QT_HOMEBREW" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "darwin", "DESKTOP" => "true", "QT_HOMEBREW" => "true"} + end + + config.vm.define "android" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "darwin", "ANDROID" => "true"} + m.vm.provision "shell", path: "../../ci/darwin.sh", privileged: false, env: {"OS" => "darwin", "ANDROID" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "darwin", "ANDROID" => "true"} + end + + config.vm.define "ios" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "darwin", "IOS" => "true", "IOS_SIMULATOR" => "true"} + m.vm.provision "shell", path: "../../ci/darwin.sh", privileged: false, env: {"OS" => "darwin", "IOS" => "true", "IOS_SIMULATOR" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "darwin", "IOS" => "true", "IOS_SIMULATOR" => "true"} + end + + #TODO: desktop + webkit + #TODO: android + openssl + #TODO: sailfish + #TODO: macports + #TODO: nix + +end diff --git a/qt/tool-chain/vagrant/docker/Vagrantfile b/qt/tool-chain/vagrant/docker/Vagrantfile new file mode 100644 index 0000000..f445973 --- /dev/null +++ b/qt/tool-chain/vagrant/docker/Vagrantfile @@ -0,0 +1,98 @@ +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/trusty64" + config.vm.synced_folder ENV['GOPATH'], "/media/sf_GOPATH0", SharedFoldersEnableSymlinksCreate: false + config.ssh.keep_alive = true + + config.vm.provider "virtualbox" do |v| + v.linked_clone = true + v.memory = 2048 + v.cpus = 1 + end + + config.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "DOCKER" => "true"} + + config.vm.define "linux" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:linux" + end + end + + config.vm.define "android" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:android" + end + end + + config.vm.define "windows_32_shared" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:windows_32_shared" + end + end + + config.vm.define "windows_32_static" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:windows_32_static" + end + end + + config.vm.define "windows_64_shared" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:windows_64_shared" + end + end + + config.vm.define "windows_64_static" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:windows_64_static" + end + end + + config.vm.define "rpi1" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:rpi1" + end + end + + config.vm.define "rpi2" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:rpi2" + end + end + + config.vm.define "rpi3" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:rpi3" + end + end + + config.vm.define "sailfish" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:sailfish" + end + end + + config.vm.define "ubports_arm_vivid" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:ubports_arm_vivid" + end + end + + config.vm.define "ubports_64_vivid" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:ubports_64_vivid" + end + end + + config.vm.define "ubports_arm_xenial" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:ubports_arm_xenial" + end + end + + config.vm.define "ubports_64_xenial" do |m| + config.vm.provision "docker" do |d| + d.pull_images "therecipe/qt:ubports_64_xenial" + end + end + +end diff --git a/qt/tool-chain/vagrant/linux/Vagrantfile b/qt/tool-chain/vagrant/linux/Vagrantfile new file mode 100644 index 0000000..bac65fd --- /dev/null +++ b/qt/tool-chain/vagrant/linux/Vagrantfile @@ -0,0 +1,64 @@ +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/xenial64" + config.vm.synced_folder ENV['GOPATH'], "/media/sf_GOPATH0", SharedFoldersEnableSymlinksCreate: false + config.ssh.keep_alive = true + + config.vm.provider "virtualbox" do |v| + v.linked_clone = true + v.memory = 8192 + v.customize ["modifyvm", :id, "--vram", "256"] + v.cpus = 1 + v.customize ["modifyvm", :id, "--accelerate3d", "on"] + end + + config.vm.define "linux" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "DESKTOP" => "true"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "DESKTOP" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "DESKTOP" => "true"} + end + + #TODO: partially broken + config.vm.define "pkg_config" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "DESKTOP" => "true", "QT_PKG_CONFIG" => "true"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "DESKTOP" => "true", "QT_PKG_CONFIG" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "DESKTOP" => "true", "QT_PKG_CONFIG" => "true"} + end + + config.vm.define "android" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "ANDROID" => "true"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "ANDROID" => "true"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "ANDROID" => "true"} + end + + config.vm.define "windows_32_shared" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "false", "QT_MXE_ARCH" => "386"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "false", "QT_MXE_ARCH" => "386"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "false", "QT_MXE_ARCH" => "386"} + end + + config.vm.define "windows_32_static" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "true", "QT_MXE_ARCH" => "386"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "true", "QT_MXE_ARCH" => "386"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "true", "QT_MXE_ARCH" => "386"} + end + + config.vm.define "windows_64_shared" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "false", "QT_MXE_ARCH" => "amd64"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "false", "QT_MXE_ARCH" => "amd64"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "false", "QT_MXE_ARCH" => "amd64"} + end + + config.vm.define "windows_64_static" do |m| + m.vm.provision "shell", path: "../pre.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "true", "QT_MXE_ARCH" => "amd64"} + m.vm.provision "shell", path: "../../ci/linux.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "true", "QT_MXE_ARCH" => "amd64"} + m.vm.provision "shell", path: "../past.sh", privileged: false, env: {"OS" => "linux", "QT_MXE" => "true", "QT_MXE_STATIC" => "true", "QT_MXE_ARCH" => "amd64"} + end + + #TODO: desktop + webkit + #TODO: android + openssl + #TODO: sailfish + #TODO: rpi + #TODO: asteroid + #TODO: ubports + +end diff --git a/qt/tool-chain/vagrant/past.sh b/qt/tool-chain/vagrant/past.sh new file mode 100755 index 0000000..7fae55f --- /dev/null +++ b/qt/tool-chain/vagrant/past.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -ev + +if [[ "$DESKTOP" == "true" ]]; then $GOPATH/bin/qtsetup full desktop; fi +if [[ "$ANDROID" == "true" ]]; then $GOPATH/bin/qtsetup full android; fi +if [[ "$IOS" == "true" ]]; then $GOPATH/bin/qtsetup full ios; fi +if [[ "$IOS_SIMULATOR" == "true" ]]; then $GOPATH/bin/qtsetup full ios-simulator; fi +if [[ "$QT_MXE" == "true" ]]; then $GOPATH/bin/qtsetup full windows; fi + +exit 0 diff --git a/qt/tool-chain/vagrant/pre.bat b/qt/tool-chain/vagrant/pre.bat new file mode 100644 index 0000000..262dde8 --- /dev/null +++ b/qt/tool-chain/vagrant/pre.bat @@ -0,0 +1,175 @@ + +::enable delayed expansion +setlocal enabledelayedexpansion + + +::disable updates +reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v AUOptions /t REG_DWORD /d 1 /f +sc config wuauserv start= disabled + + +::install curl +expand -f:* c:\tmp\curl.cab c:\tmp\ +mv -f /cygdrive/c/tmp/AMD64/* "/cygdrive/c/Program Files/OpenSSH/bin/" + + +::install 7z +set SZ=7z1805-x64.exe +curl -sL --retry 10 --retry-delay 10 -o %TMP%\%SZ% https://7-zip.org/a/%SZ% +%TMP%\%SZ% /S +del %TMP%\%SZ% /Q +setx /M PATH "%PATH%;C:\Progra~1\7-Zip" +set PATH=%PATH%;C:\Progra~1\7-Zip + + +::install Git +set GIT=Git-2.19.0-64-bit.exe +curl -sL --retry 10 --retry-delay 10 -o %TMP%\%GIT% https://github.com/git-for-windows/git/releases/download/v2.19.0.windows.1/%GIT% +%TMP%\%GIT% /silent /norestart +del %TMP%\%GIT% /Q +setx /M PATH "%PATH%;C:\Progra~1\Git\bin" +set PATH=%PATH%;C:\Progra~1\Git\bin + + +::install Go + pull repo +set GO=go1.11.2.windows-amd64.msi +curl -sL --retry 10 --retry-delay 10 -o %TMP%\%GO% https://storage.googleapis.com/golang/%GO% +%TMP%\%GO% /passive /norestart +del %TMP%\%GO% /Q +reg delete "HKEY_CURRENT_USER\Environment" /v GOPATH /f +setx /M PATH "%PATH%;C:\Go\bin" +set PATH=%PATH%;C:\Go\bin +setx /M GOPATH "C:\gopath" +set GOPATH=C:\gopath +setx /M GOROOT "C:\go" +set GOROOT=C:\go + +go get -v -tags=no_env github.com/peterq/pan-light/qt/cmd/... + + +::install VC++ 2015 Redis +set VC=vc_redist.x64.exe +curl -sL --retry 10 --retry-delay 10 -o %TMP%\%VC% https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/%VC% +%TMP%\%VC% /passive /norestart +del %TMP%\%VC% /Q + + +if "%QT_MSYS2%" == "true" ( + setx /M QT_MSYS2 "%QT_MSYS2%" + setx /M QT_MSYS2_STATIC "%QT_MSYS2_STATIC%" + setx /M QT_MSYS2_ARCH "%QT_MSYS2_ARCH%" + + if "%QT_MSYS2_ARCH%" == "386" ( + setx /M MSYSTEM "MINGW32" + echo MSYSTEM=MINGW32>> C:\Users\vagrant\.ssh\environment + ) else ( + setx /M MSYSTEM "MINGW64" + echo MSYSTEM=MINGW64>> C:\Users\vagrant\.ssh\environment + ) + + echo QT_MSYS2=true>> C:\Users\vagrant\.ssh\environment + echo QT_MSYS2_STATIC=%QT_MSYS2_STATIC%>> C:\Users\vagrant\.ssh\environment + echo QT_MSYS2_ARCH=%QT_MSYS2_ARCH%>> C:\Users\vagrant\.ssh\environment + + + ::install msys2 + set MSYS2=msys2-x86_64-20180531.exe + set AI=auto-install.js + curl -sL --retry 10 --retry-delay 10 -o %TMP%\!MSYS2! http://repo.msys2.org/distrib/x86_64/!MSYS2! + curl -sL --retry 10 --retry-delay 10 -o %TMP%\!AI! https://raw.githubusercontent.com/msys2/msys2-installer/master/!AI! + %TMP%\!MSYS2! --script %TMP%\!AI! + del %TMP%\!MSYS2! /Q + del %TMP%\!AI! /Q + + + C:\msys64\usr\bin\bash -l -c "pacman -Q" + C:\msys64\usr\bin\bash -l -c "pacman -Syyu --noconfirm --noprogress" + C:\msys64\usr\bin\bash -l -c "pacman -Syyu --noconfirm --noprogress" + + if "%QT_MSYS2_ARCH%" == "386" ( + if "%QT_MSYS2_STATIC%" == "true" ( + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm --needed --noprogress mingw-w64-i686-qt-creator mingw-w64-i686-qt5-static" + ) else ( + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm --needed --noprogress mingw-w64-i686-qt-creator mingw-w64-i686-qt5" + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm --needed --noprogress mingw-w64-i686-qtwebkit" + ) + ) else ( + if "%QT_MSYS2_STATIC%" == "true" ( + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm --needed --noprogress mingw-w64-x86_64-qt-creator mingw-w64-x86_64-qt5-static" + ) else ( + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm --needed --noprogress mingw-w64-x86_64-qt-creator mingw-w64-x86_64-qt5" + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm --needed --noprogress mingw-w64-x86-qtwebkit" + ) + ) + + C:\msys64\usr\bin\bash -l -c "pacman -Q" + C:\msys64\usr\bin\bash -l -c "pacman -Scc --noconfirm --noprogress" +) else ( + ::install Qt + set QT=qt-unified-windows-x86-online.exe + curl -sL --retry 10 --retry-delay 10 -o %TMP%\!QT! https://download.qt.io/official_releases/online_installers/!QT! + %TMP%\!QT! -v --script %GOPATH%\src\github.com\therecipe\qt\internal\ci\iscript.qs WINDOWS=true + del %TMP%\!QT! /Q + setx /M PATH "%PATH%;C:\Qt\Tools\mingw730_64\bin" + set PATH=%PATH%;C:\Qt\Tools\mingw730_64\bin +) + + +::update ssh env variables +echo TMP=C:/tmp>> C:\Users\vagrant\.ssh\environment +net stop "OpenSSH Server" +net start "OpenSSH Server" + + +if "%ANDROID%" == "true" ( + ::install JDK + set JDK=jdk-8u192-ea-bin-b04-windows-x64-01_aug_2018.exe + curl -sL --retry 10 --retry-delay 10 -o %TMP%\!JDK! https://download.java.net/java/jdk8u192/archive/b04/binaries/!JDK! + %TMP%\!JDK! /s + del %TMP%\!JDK! /Q + + setx /M JAVA_HOME "C:\Progra~1\Java\jdk1.8.0_192" + set JAVA_HOME=C:\Progra~1\Java\jdk1.8.0_192 + + ::install Android SDK + set SDK=sdk-tools-windows-4333796.zip + curl -sL --retry 10 --retry-delay 10 -o %TMP%\!SDK! https://dl.google.com/android/repository/!SDK! + 7z x %TMP%\!SDK! -oC:\android-sdk-windows\ + del %TMP%\!SDK! /Q + + mkdir C:\android-sdk-windows\licenses + echo fc946e8f231f3e3159bf0b7c655c924cb2e38330>> C:\android-sdk-windows\licenses\android-googletv-license + echo d56f5187479451eabf01fb78af6dfcb131a6481e>> C:\android-sdk-windows\licenses\android-sdk-license + echo 504667f4c0de7af1a06de9f4b1727b84351f2910>> C:\android-sdk-windows\licenses\android-sdk-preview-license + echo 33b6a2b64607f11b759f320ef9dff4ae5c47d97a>> C:\android-sdk-windows\licenses\google-gdk-license + echo d975f751698a77b662f1254ddbeed3901e976f5a>> C:\android-sdk-windows\licenses\intel-android-extra-license + echo 63d703f5692fd891d5acacfbd8e09f40fc976105>> C:\android-sdk-windows\licenses\mips-android-sysimage-license + + cmd /C "C:\android-sdk-windows\tools\bin\sdkmanager.bat --list --verbose" + cmd /C "C:\android-sdk-windows\tools\bin\sdkmanager.bat "platform-tools" "build-tools;26.0.0" "platforms;android-25"" + cmd /C "mv C:\android-sdk-windows\tools\ C:\android-sdk-windows\toolsOLD\" + cmd /C "C:\android-sdk-windows\toolsOLD\bin\sdkmanager.bat --update" + cmd /C "rm -R C:\android-sdk-windows\toolsOLD\" + + + ::install Android NDK + set NDK=android-ndk-r18b-windows-x86_64.zip + curl -sL --retry 10 --retry-delay 10 -o %TMP%\!NDK! https://dl.google.com/android/repository/!NDK! + 7z x %TMP%\!NDK! -oC:\ + del %TMP%\!NDK! /Q +) + + +::qtsetup +if "%QT_MSYS2%" == "true" ( + if "%QT_MSYS2_ARCH%" == "386" ( + set MSYSTEM=MINGW32 + C:\msys64\usr\bin\bash -l -c "$GOPATH/bin/qtsetup full desktop" + ) else ( + set MSYSTEM=MINGW64 + C:\msys64\usr\bin\bash -l -c "$GOPATH/bin/qtsetup full desktop" + ) +) else ( + if "%DESKTOP%" == "true" "%GOPATH%\bin\qtsetup" full desktop + if "%ANDROID%" == "true" "%GOPATH%\bin\qtsetup" full android +) diff --git a/qt/tool-chain/vagrant/pre.sh b/qt/tool-chain/vagrant/pre.sh new file mode 100755 index 0000000..675ac85 --- /dev/null +++ b/qt/tool-chain/vagrant/pre.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -ev + +if [[ "$OS" == "darwin" ]]; then + export PROF=.bash_profile + export GO=go1.11.2.darwin-amd64.tar.gz +else if [[ "$OS" == "linux" ]]; then + export PROF=.profile + export GO=go1.11.2.linux-amd64.tar.gz + + sudo apt-get -qq update && sudo apt-get -y -qq install curl git software-properties-common libgl1-mesa-dev fontconfig unzip && sudo apt-get -qq clean + + if false; then + sudo apt-get -qq update && sudo apt-get -y -qq install bison build-essential gperf flex ruby python libasound2-dev libbz2-dev libcap-dev libcups2-dev libdrm-dev libegl1-mesa-dev libgcrypt11-dev libnss3-dev libpci-dev libpulse-dev libudev-dev libxtst-dev gyp ninja-build && sudo apt-get -qq clean + sudo apt-get -qq update && sudo apt-get -y -qq install libssl-dev libxcursor-dev libxcomposite-dev libxdamage-dev libxrandr-dev libfontconfig1-dev libxss-dev libsrtp0-dev libwebp-dev libjsoncpp-dev libopus-dev libavutil-dev libavformat-dev libavcodec-dev libevent-dev libxslt1-dev && sudo apt-get -qq clean + + sudo apt-get -qq update && sudo apt-get -y -qq install lxde xinit && sudo apt-get -qq clean + sudo /usr/share/debconf/fix_db.pl #or sudo apt-get -y -qq remove miscfiles dictionaries-common + echo "exec startlxde" >> $HOME/.xinitrc + sudo startx & + fi +fi; fi + +#darwin +if [[ "$QT_HOMEBREW" == "true" ]]; then echo "export QT_HOMEBREW=true" >> $HOME/$PROF; fi + +#linux +if [[ "$QT_PKG_CONFIG" == "true" ]]; then + echo "export QT_PKG_CONFIG=true" >> $HOME/$PROF + echo "export PKG_CONFIG_PATH=/opt/qt510/lib/pkgconfig" >> $HOME/$PROF + echo "export QT_DOC_DIR=/opt/qt510/doc" >> $HOME/$PROF + echo "export QT_MISC_DIR=/opt/qt510" >> $HOME/$PROF +fi + +if [[ "$QT_MXE" == "true" ]]; then + echo "export QT_MXE_ARCH="$QT_MXE_ARCH >> $HOME/$PROF + echo "export QT_MXE_STATIC="$QT_MXE_STATIC >> $HOME/$PROF +fi + +curl -sL --retry 10 --retry-delay 10 -o /tmp/$GO https://dl.google.com/go/$GO && tar -xzf /tmp/$GO -C $HOME && rm -f /tmp/$GO + +echo "export PATH=$PATH:$HOME/go/bin" >> $HOME/$PROF +echo "export GOROOT=$HOME/go" >> $HOME/$PROF +echo "export GOPATH=$HOME/gopath" >> $HOME/$PROF +source $HOME/$PROF + +if [[ "$OS" == "darwin" ]]; then + ln -s $HOME/go $HOME/Desktop/GOROOT + ln -s $HOME/gopath $HOME/Desktop/GOPATH +fi + +go get -v -tags=no_env github.com/peterq/pan-light/qt/cmd/... + +if [[ "$OS" == "darwin" ]]; then + sudo xcode-select -s /Applications/Xcode.app/Contents/Developer + if [[ "$IOS" == "true" ]]; then rm -R /Applications/Xcode.app/Contents/Developer/Platforms/AppleTVOS.platform; rm -R /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform; fi + if [[ "$ANDROID" == "true" ]]; then brew update; brew tap caskroom/versions; brew cask install java8; fi +else if [[ "$OS" == "linux" ]]; then + sudo rm -f -R $HOME/.config + sudo rm -f -R $HOME/.cache +fi; fi + +$GOPATH/bin/qtsetup prep + +exit 0 diff --git a/qt/tool-chain/vagrant/windows/Vagrantfile b/qt/tool-chain/vagrant/windows/Vagrantfile new file mode 100644 index 0000000..0827589 --- /dev/null +++ b/qt/tool-chain/vagrant/windows/Vagrantfile @@ -0,0 +1,48 @@ +Vagrant.configure("2") do |config| + config.vm.box = "designerror/windows-7" + config.vm.synced_folder ENV['GOPATH'], "/media/sf_GOPATH0", SharedFoldersEnableSymlinksCreate: false + config.ssh.keep_alive = true + + config.vm.provider "virtualbox" do |v| + v.linked_clone = true + v.memory = 8192 + v.customize ["modifyvm", :id, "--vram", "256"] + v.cpus = 1 + v.customize ["modifyvm", :id, "--accelerate3d", "on"] + end + + config.vm.define "windows" do |m| + config.vm.provision "shell", inline: "(New-Object Net.WebClient).DownloadFile('https://skanthak.homepage.t-online.de/download/curl-7.61.1.cab', 'c:/tmp/curl.cab')" + config.vm.provision "shell", path: "../pre.bat", env: {"OS" => "windows", "DESKTOP" => "true"} + end + + config.vm.define "android" do |m| + config.vm.provision "shell", inline: "(New-Object Net.WebClient).DownloadFile('https://skanthak.homepage.t-online.de/download/curl-7.61.1.cab', 'c:/tmp/curl.cab')" + config.vm.provision "shell", path: "../pre.bat", env: {"OS" => "windows", "ANDROID" => "true"} + end + + config.vm.define "windows_32_shared" do |m| + config.vm.provision "shell", inline: "(New-Object Net.WebClient).DownloadFile('https://skanthak.homepage.t-online.de/download/curl-7.61.1.cab', 'c:/tmp/curl.cab')" + config.vm.provision "shell", path: "../pre.bat", env: {"OS" => "windows", "QT_MSYS2" => "true", "QT_MSYS2_STATIC" => "false", "QT_MSYS2_ARCH" => "386"} + end + + config.vm.define "windows_32_static" do |m| + config.vm.provision "shell", inline: "(New-Object Net.WebClient).DownloadFile('https://skanthak.homepage.t-online.de/download/curl-7.61.1.cab', 'c:/tmp/curl.cab')" + config.vm.provision "shell", path: "../pre.bat", env: {"OS" => "windows", "QT_MSYS2" => "true", "QT_MSYS2_STATIC" => "true", "QT_MSYS2_ARCH" => "386"} + end + + config.vm.define "windows_64_shared" do |m| + config.vm.provision "shell", inline: "(New-Object Net.WebClient).DownloadFile('https://skanthak.homepage.t-online.de/download/curl-7.61.1.cab', 'c:/tmp/curl.cab')" + config.vm.provision "shell", path: "../pre.bat", env: {"OS" => "windows", "QT_MSYS2" => "true", "QT_MSYS2_STATIC" => "false", "QT_MSYS2_ARCH" => "amd64"} + end + + config.vm.define "windows_64_static" do |m| + config.vm.provision "shell", inline: "(New-Object Net.WebClient).DownloadFile('https://skanthak.homepage.t-online.de/download/curl-7.61.1.cab', 'c:/tmp/curl.cab')" + config.vm.provision "shell", path: "../pre.bat", env: {"OS" => "windows", "QT_MSYS2" => "true", "QT_MSYS2_STATIC" => "true", "QT_MSYS2_ARCH" => "amd64"} + end + + #TODO: desktop + webkit + #TODO: android + openssl + #TODO: sailfish + +end