Index: find-modules/module_generation/.gitignore =================================================================== --- /dev/null +++ find-modules/module_generation/.gitignore @@ -0,0 +1 @@ +*.pyc Index: find-modules/module_generation/CMakeLists.txt =================================================================== --- /dev/null +++ find-modules/module_generation/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# This is a helper script used to locate binaries, libraries and other +# platform-dependent items for use by the Python code. The output is +# NOT CMakeCache.txt et. al. but a JSON file: +# +# { +# "SIP_EXECUTABLE": "/usr/bin/sip", +# "ClangPP_EXECUTABLE": "/usr/bin/clang++-3.9", +# "ClangPP_SYS_INCLUDES": "/usr/bin/...;/usr/bin/...", +# "LibClang_LIBRARY": "/usr/lib/x86_64-linux-gnu/libclang-3.9.so", +# "$": null +# } +# +# The "$" dict entries are a convenience which the caller discards. +# + +cmake_minimum_required(VERSION 3.7) + +project(sip_module_generation) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +find_package(SIP REQUIRED) +find_package(Clang REQUIRED) +find_package(LibClang REQUIRED) + +if(NOT ClangPP_FOUND) + message(SEND_ERROR "Clang++ not found") +elseif(SIP_VERSION VERSION_LESS 4.19.3) + message(SEND_ERROR "SIP version \"${SIP_VERSION}\" too old") +elseif(ClangPP_VERSION VERSION_LESS 3.9) + message(SEND_ERROR "Clang++ version \"${ClangPP_VERSION}\" too old") +elseif(LibClang_VERSION VERSION_LESS 3.9) + message(SEND_ERROR "LibClang version \"${LibClang_VERSION}\" too old") +else() + # + # Find the system includes used by clang++. This is modelled on a part of Steve Kelley's + # FindPythonModuleGeneration.cmake. + # + execute_process(COMMAND ${ClangPP_EXECUTABLE} -v -E -x c++ - + ERROR_VARIABLE _compilerOutput + OUTPUT_VARIABLE _compilerStdout + INPUT_FILE /dev/null) + if("${_compilerOutput}" MATCHES "> search starts here[^\n]+\n *(.+ *\n) *End of (search) list") + # split the output into lines and then remove leading and trailing spaces from each of them: + string(REGEX MATCHALL "[^\n]+\n" _includeLines "${CMAKE_MATCH_1}") + foreach(nextLine ${_includeLines}) + # on OSX, gcc says things like this: "/System/Library/Frameworks (framework directory)", strip the last part + string(REGEX REPLACE "\\(framework directory\\)" "" nextLineNoFramework "${nextLine}") + # strip spaces at the beginning and the end + string(STRIP "${nextLineNoFramework}" _includePath) + list(APPEND ClangPP_SYS_INCLUDES "${_includePath}") + endforeach() + endif() + # + # Write the values we found as a JSON blob. + # + set(dict ${CMAKE_CURRENT_BINARY_DIR}/configure.json) + message(STATUS "Writing ${dict}") + file(WRITE "${dict}" "{\n") + foreach(var "SIP_EXECUTABLE" "ClangPP_EXECUTABLE" "ClangPP_SYS_INCLUDES" "LibClang_LIBRARY") + file(APPEND "${dict}" "\"${var}\": \"${${var}}\",\n") + endforeach() + file(APPEND "${dict}" "\"$\": null\n") + file(APPEND "${dict}" "}\n") +endif() Index: find-modules/module_generation/COPYING.LIB =================================================================== --- /dev/null +++ find-modules/module_generation/COPYING.LIB @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. 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 not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the 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 +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + Index: find-modules/module_generation/FindClang.cmake =================================================================== --- /dev/null +++ find-modules/module_generation/FindClang.cmake @@ -0,0 +1,93 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#.rst: +# FindClang +# --------- +# +# Find Clang +# +# This module finds an installed Clang. It sets the following variables: +# +# :: +# +# Clang_FOUND - set to true if clang is found +# Clang_DIR - the directory where clang is installed +# Clang_EXECUTABLE - the path to the clang executable +# Clang_VERSION - Version number of the clang executable as a string (e.g. "3.9") +# ClangPP_FOUND - set to true if clang++ is found +# ClangPP_DIR - the directory where clang++ is installed +# ClangPP_EXECUTABLE - the path to the clang++ executable +# ClangPP_VERSION - Version number of the clang++ executable as a string (e.g. "3.9") +# +# The minimum required version of Clang can be specified using the +# standard syntax, e.g. find_package(Clang 4.19) +# +# All information is collected from the Clang/PP_EXECUTABLE so the version +# to be found can be changed from the command line by means of setting +# Clang/PP_EXECUTABLE + +find_program(Clang_EXECUTABLE NAMES clang-3.9) + +if(Clang_EXECUTABLE) + execute_process(COMMAND ${Clang_EXECUTABLE} -v + OUTPUT_VARIABLE Clang_version_output + ERROR_VARIABLE Clang_version_output + RESULT_VARIABLE Clang_version_result) + + if(Clang_version_result) + message(SEND_ERROR "Command \"${Clang_EXECUTABLE} -v\" failed with output:\n${Clang_version_output}") + else() + string(REGEX REPLACE "clang version[ ]+\([0-9]+.[0-9]+.[^ ]+\)[ ]+.*" "\\1" Clang_version_output ${Clang_version_output}) + set(Clang_VERSION ${Clang_version_output} CACHE STRING "Clang version" FORCE) + endif() +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clang REQUIRED_VARS Clang_EXECUTABLE + VERSION_VAR Clang_VERSION) + + +find_program(ClangPP_EXECUTABLE NAMES clang++-3.9) + +if(ClangPP_EXECUTABLE) + execute_process(COMMAND ${ClangPP_EXECUTABLE} -v + OUTPUT_VARIABLE ClangPP_version_output + ERROR_VARIABLE ClangPP_version_output + RESULT_VARIABLE ClangPP_version_result) + + if(ClangPP_version_result) + message(SEND_ERROR "Command \"${ClangPP_EXECUTABLE} -v\" failed with output:\n${ClangPP_version_output}") + else() + string(REGEX REPLACE "clang version[ ]+\([0-9]+.[0-9]+.[^ ]+\)[ ]+.*" "\\1" ClangPP_version_output ${ClangPP_version_output}) + set(ClangPP_VERSION ${ClangPP_version_output} CACHE STRING "ClangPP version" FORCE) + endif() +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ClangPP REQUIRED_VARS ClangPP_EXECUTABLE + VERSION_VAR ClangPP_VERSION) + +mark_as_advanced(Clang_VERSION ClangPP_VERSION) Index: find-modules/module_generation/FindLibClang.cmake =================================================================== --- /dev/null +++ find-modules/module_generation/FindLibClang.cmake @@ -0,0 +1,52 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#.rst: +# FindLibClang +# ------------ +# +# Find LibClang +# +# Find LibClang headers and library +# +# :: +# +# LibClang_FOUND - True if libclang is found. +# LibClang_LIBRARY - Clang library to link against. +# LibClang_VERSION - Version number as a string (e.g. "3.9") + +find_library(LibClang_LIBRARY clang-3.9) + +if(LibClang_LIBRARY) + set(LibClang_LIBRARY ${LibClang_LIBRARY}) + string(REGEX REPLACE ".*clang-\([0-9]+.[0-9]+\).*" "\\1" LibClang_VERSION_TMP "${LibClang_LIBRARY}") + set(LibClang_VERSION ${LibClang_VERSION_TMP} CACHE STRING "LibClang version" FORCE) +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibClang REQUIRED_VARS LibClang_LIBRARY + VERSION_VAR LibClang_VERSION) + +mark_as_advanced(LibClang_VERSION) Index: find-modules/module_generation/FindSIP.cmake =================================================================== --- /dev/null +++ find-modules/module_generation/FindSIP.cmake @@ -0,0 +1,122 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#.rst: +# FindSIP +# ------- +# +# Find SIP +# +# This module finds an installed SIP. It sets the following variables: +# +# :: +# +# SIP_FOUND - set to true if SIP is found +# SIP_DIR - the directory where SIP is installed +# SIP_EXECUTABLE - the path to the SIP executable +# SIP_INCLUDE_DIRS - Where to find sip.h. +# SIP_VERSION - the version number of the SIP executable +# +# +# +# The minimum required version of SIP can be specified using the +# standard syntax, e.g. find_package(SIP 4.19) +# +# All information is collected from the SIP_EXECUTABLE so the version +# to be found can be changed from the command line by means of setting +# SIP_EXECUTABLE + +find_program(SIP_EXECUTABLE NAMES sip) + +if(SIP_EXECUTABLE) + execute_process(COMMAND ${SIP_EXECUTABLE} -V + OUTPUT_VARIABLE SIP_version_output + ERROR_VARIABLE SIP_version_output + RESULT_VARIABLE SIP_version_result) + + if(SIP_version_result) + message(SEND_ERROR "Command \"${SIP_EXECUTABLE} -V\" failed with output:\n${SIP_version_output}") + else() + string(REGEX REPLACE "[\n\r]+" "" SIP_version_output ${SIP_version_output}) + set(SIP_VERSION ${SIP_version_output} CACHE STRING "SIP version" FORCE) + endif() +endif() + +# +# See https://github.com/pybind/pybind11/blob/master/tools/FindPythonLibsNew.cmake. +# +find_package(PythonInterp QUIET REQUIRED) +find_package(PythonLibs QUIET REQUIRED) +execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(sys.prefix); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +print(s.get_config_var('LIBDIR') or ''); +print(s.get_config_var('MULTIARCH') or ''); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) +# +# Convert the process output into a list. +# +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) +list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) +list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) +list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) +# +# Find sip.h. +# +find_file(SIP_HEADER NAMES sip.h + HINTS "${PYTHON_INCLUDE_DIR}") +if(SIP_HEADER) + set(SIP_INCLUDE_DIRS ${PYTHON_INCLUDE_DIR}) + file(STRINGS "${SIP_HEADER}" SIP_VERSION_TMP REGEX ".*SIP_VERSION_STR.*\"\([0-9]+.[0-9]+\.*)\"") + string(REGEX REPLACE ".*SIP_VERSION_STR.*\"\([0-9]+.[0-9]+\.*)\"" "\\1" SIP_VERSION_TMP "${SIP_VERSION_TMP}") + set(SIP_HEADER_VERSION ${SIP_VERSION_TMP} CACHE STRING "LibSIP version" FORCE) + if(NOT SIP_VERSION STREQUAL SIP_HEADER_VERSION) + message(SEND_ERROR "Command version \"${SIP_VERSION}\" does not match header version ${SIP_HEADER_VERSION}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SIP REQUIRED_VARS SIP_EXECUTABLE SIP_INCLUDE_DIRS + VERSION_VAR SIP_VERSION) + +mark_as_advanced(SIP_VERSION) Index: find-modules/module_generation/HOWTO =================================================================== --- /dev/null +++ find-modules/module_generation/HOWTO @@ -0,0 +1,191 @@ +Pre-requisites +============== + +PyPI: sudo pip install +---------------------- + + # + # Core system dependencies. + # + pcpp + +Ubuntu/Debian "sudo apt install" +-------------------------------- + + # + # Core system dependencies. + # + cmake # For cmake + python-clang-3.9 libclang-3.9 # For libclang + clang++-3.9 # For clang++ + sip-dev python-sip-dev # For sip.h + # + # Regression test basics. + # + gettext + extra-cmake-modules + pyqt5-dev python-pyqt5 # For PyQt + qtbase5-dev # For QtCore + qttools5-dev # For kwidgetsaddons + kdoctools-dev # For kconfigwidgets + qtdeclarative5-dev # For ki18n + libqt5x11extras5-dev # For kjobwidgets + libmarble-dev # For KGeoMap + # + # Regression test packages. + # + kio-dev + kross-dev + libkf5activities-dev + libkf5activitiesstats-dev + libkf5akonadi-dev + libkf5akonadinotes-dev + libkf5akonadimime-dev + libkf5akonadicalendar-dev + libkf5akonadicontact-dev + libkf5akonadisocialutils-dev + libkf5akonadisearch-dev + libkf5alarmcalendar-dev + libkf5archive-dev + libkf5attica-dev + libkf5auth-dev + libkf5baloowidgets-dev + libkf5blog-dev + libkf5bluezqt-dev + libkf5bookmarks-dev + libkf5calendarcore-dev + libkf5calendarsupport-dev + libkf5calendarutils-dev + libkf5codecs-dev + libkf5completion-dev + libkf5composereditorng-dev + libkf5config-dev + libkf5configwidgets-dev + libkf5contacts-dev + libkf5coreaddons-dev + libkf5crash-dev + libkf5dbusaddons-dev + libkf5declarative-dev + libkf5dnssd-dev + libkf5emoticons-dev + libkf5eventviews-dev + libkf5filemetadata-dev + libkf5followupreminder-dev + libkf5gapi-dev + libkf5globalaccel-dev + libkf5gpgmepp-dev + libkf5grantleetheme-dev + libkf5gravatar-dev + libkf5guiaddons-dev + libkf5holidays-dev + libkf5i18n-dev + libkf5iconthemes-dev + libkf5identitymanagement-dev + libkf5idletime-dev + libkf5imap-dev + libkf5incidenceeditor-dev + libkf5itemmodels-dev + libkf5itemviews-dev + libkf5jobwidgets-dev + libkf5jsembed-dev + libkf5kaddressbookgrantlee-dev + libkf5kcmutils-dev + libkf5kdcraw-dev + libkf5kdegames-dev + libkf5kdelibs4support-dev + libkf5kdepimdbusinterfaces-dev + libkf5kdgantt2-dev + libkf5kexiv2-dev + libkf5kface-dev + libkf5kgeomap-dev + libkf5khtml-dev + libkf5kipi-dev + libkf5kjs-dev + libkf5kmahjongglib-dev + libkf5kontactinterface-dev + libkf5ksieve-dev + libkf5ldap-dev + libkf5libkdepim-dev + libkf5libkleo-dev + libkf5mailcommon-dev + libkf5mailimporter-dev + libkf5mailtransport-dev + libkf5mbox-dev + libkf5mediaplayer-dev + libkf5messagecomposer-dev + libkf5messagecore-dev + libkf5messagelist-dev + libkf5messageviewer-dev + libkf5mime-dev + libkf5networkmanagerqt-dev + libkf5newstuff-dev + libkf5notifications-dev + libkf5notifyconfig-dev + libkf5package-dev + libkf5parts-dev + libkf5people-dev + libkf5pimcommon-dev + libkf5pimtextedit-dev + libkf5plotting-dev + libkf5prison-dev + libkf5pty-dev + libkf5purpose-dev + libkf5runner-dev + libkf5sane-dev + libkf5screen-dev + libkf5sendlater-dev + libkf5service-dev + libkf5solid-dev + libkf5sonnet-dev + libkf5style-dev + libkf5su-dev + libkf5syndication-dev + libkf5templateparser-dev + libkf5texteditor-dev + libkf5textwidgets-dev + libkf5threadweaver-dev + libkf5tnef-dev + libkf5unitconversion-dev + libkf5wallet-dev + libkf5webkit-dev + libkf5widgetsaddons-dev + libkf5windowsystem-dev + libkf5xmlgui-dev + libkf5xmlrpcclient-dev + plasma-framework-dev + +To build PyKF5 +============== + + # + # 1. Run the SIP generation phase. + # + ./module_generator.py tmp + # + # 2. Run the compiler... + # + ./module_compiler.py tmp result + +The output is: + + # A "tmp" directory containing intermediate files. + # A "results/python/PyKF5" directory with the Python bindings: + * An __init__.py. + * All the .so files such as KItemModels.so. + # A "results/sip/PyKF5" directory with: + * The shippable SIP files. + * A modules.feature file. + +To build your own bindings +========================== + +See the output of running the rules_engine.py as a main program: + + ./rules_engine.py + +Run the sip_bulk_generator.py and the sip_compiler.py as above but with command +line options overriding the defaults (which are intended for PyKF5 and similar +frameworks). Optionally, use sip_generator.py if you want a more manual +approach. + +Each tool has online help accessed via the -h option. Index: find-modules/module_generation/HOWTO.WRITE_RULES =================================================================== --- /dev/null +++ find-modules/module_generation/HOWTO.WRITE_RULES @@ -0,0 +1,120 @@ +Here are some handy hints for rule writers, in FAQ form... + +0. My rule is not firing, how I can debug this? +=============================================== + +Try the following steps: + +- Review the help text output by running "./rule_engine.py --help". Did you + get the fields in your entry in the right order? + +- The first field can be a bit tricky to get right. For the exact name that + should be used, run "./module_generator.py" with the "--dump-items" option. + +- Review the documentation on Python regular expressions. Note that when rule + matching is done, multi-line entries are converted into a single line with + any line separators turned into a single space (so a function with arguments + wrapped across multipel lines is all on one line formatching purposes). + +1. I want to keep a given forward declaration +============================================= + +By default, built-in rules discard forward declarations because SIP does not +support a forward declaration followed by a real declaration is the same +module. + +(See https://www.riverbankcomputing.com/pipermail/pyqt/2017-April/039094.html). + +Override the default using a ForwardDeclarationDb rule with the noop() action. + +10. I see a SIP compilation error of the following type... +========================================================== + + 10.1. sip: ::SomeClass already defined while processing TheModulemod.sip + ======================================================================== + + Unlike C++, SIP does not like multiple declaration of the same item. One + way this can happen is if a %Import'd module contains a declaration which + is also in TheModule. + + The unwanted declarations can be fixed by using _discard(). + + 10.2. sip: tmp/some.sip:42: The struct/class has already been defined while processing TheModulemod.sip + ======================================================================================================= + + Unlike C++, SIP does not like multiple declaration of the same item. One + way this can happen is if there are multiple declarations in the TheModule + module. + + The unwanted declarations can be fixed by using _discard(). Note that + the individual SIP files are %Include'd in alphabetical order. A tricky + situation arises where, for example, duplicate typedefs occur in the same + namespace; these extreme cases cannot be handled by the regular expression + matching for rules, instead, use logic like this: + + def typedef_duplicate_discard(container, typedef, sip, rule): + if container.translation_unit.spelling.endswith("storejob.h"): + rule_helpers.typedef_discard(container, typedef, sip, rule) + else: + return rule_helpers.SILENT_NOOP + + See also FAQ #10.20. + + 10.20. sip: SomeClass is undefined while processing TheModulemod.sip + ==================================================================== + + One reason this can happen is because of FAQ #1. However, fixing that can + reveal errors like FAQ #10.2, and fixing that can cause this error. + + The missing declaration can be inserted using module_add_classes(). + + 10.30. sip: someMethodOrCtor argument n has an unsupported type for a Python signature... + ========================================================================================= + + SIP is very restrictive in what it will accept as a function argument type, with templates + being a particular problem even when a %MappedType is in place. + + A synthetic typedef is often needed, and can be inserted using module_add_typedefs(). + + +20. I see a C++ compilation error of the following type... +========================================================== + + 20.1. error: expected type-specifier before 'sipMyClass' + ======================================================== + + For C++ class 'MyClass', SIP sometimes creates a subclass called + 'sipMyClass', but not always. This cannot easily be detected, and can + result in this error in generated code. + + (See https://www.riverbankcomputing.com/pipermail/pyqt/2017-June/039309.html) + + This can be fixed using container_fake_derived_class(). + + 20.2. error: use of deleted function 'SomeClass& SomeClass::operator=(const SomeClass&)' + ======================================================================================== + + For C++ class 'SomeClass', SIP creates a function called 'assign_SomeClass' + but this relies on the operator= being present. C++ causes the default + operator= to be suppressed resulting in this error if a base class cannot + be so assigned. However, the built-in logic has not been able to emit + the needed private assignment operator. + + This can be fixed using container_make_unassignable(). + + 20.3. error: use of deleted function 'SomeClass(const SomeClass&)' + ================================================================== + + For C++ class 'SomeClass', the built-in logic has not been able to emit + the needed private copy constructor. + + This can be fixed using container_make_uncopyable(). + + 20.10. error: 'sipType_MyClass' was not declared in this scope + ============================================================ + + The module_generator.py logic which generates the list of modules which + must be %Import'd cannot always detect the complete list, and in this case, + a %Import is required for the module which contains MyClass. + + This can be fixed using module_add_imports(). Index: find-modules/module_generation/PyKF5/.gitignore =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/.gitignore @@ -0,0 +1 @@ +*.pyc Index: find-modules/module_generation/PyKF5/Akonadi.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Akonadi.py @@ -0,0 +1,609 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Akonadi. This modules describes: + + * Supplementary SIP file generator rules. +""" +import builtin_rules +import rule_helpers +import templates.mappedtype +from utils import trace_generated_for + + +def container_add_typedefs_imageprovider(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "KSharedPixmapCacheMixin") + + +def container_add_typedefs_imapparser(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "QVarLengthArray") + + +def _function_rewrite_using_decl(container, fn, sip, rule): + sip["parameters"] = ["const QModelIndex ¤t", "const QModelIndex &previous"] + sip["prefix"] = "virtual " + + +def _function_rewrite_using_decl2(container, fn, sip, rule): + sip["parameters"] = ["const Akonadi::Collection &collection"] + + +def _parameter_restore_default(container, fn, parameter, sip, rule): + sip["init"] = "Q_NULLPTR" + + +def _parameter_use_qstring(container, fn, parameter, sip, rule): + sip["decl"] = "const QString &" + sip["name"] + + +def _typedef_add_collections(container, typedef, sip, rule): + parameter = sip["decl"] + value = { + "type": parameter, + "base_type": parameter, + } + handler = templates.mappedtype.ListExpander(templates.PyQt.ListHelperValue) + value_h = handler.helpers["value"](value, None) + for qt_type in ["QList", "QVector"]: + mapped_type = "{}<{}>".format(qt_type, parameter) + trace = trace_generated_for(typedef, rule, {"value": value_h.category}) + code = handler.expand_generic(qt_type, {"value": value_h}) + code = "%MappedType " + mapped_type + "\n{\n" + trace + code + "};\n" + sip["modulecode"][mapped_type] = code + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, rule, "QMap") + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QSharedPointer", "QSharedPointer", + "QVector", "QVector") + rule_helpers.module_add_classes(filename, sip, rule, "Akonadi::Protocol::Command /External/", + "Akonadi::ServerManagerPrivate /External/", "KConfigGroup", "KCoreConfigSkeleton") + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types_agentbase(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSet", "QSet", + "QVector", "QVector", + "QVector", "QVector", "QVector", + "QVector") + rule_helpers.module_add_classes(filename, sip, entry, "QDBusContext /External/", "Akonadi::ImapSet", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + rule_helpers.module_add_imports(filename, sip, entry, "QtDBus/QtDBusmod.sip") + + +def module_fix_mapped_types_calendar(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSet", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QVector", + "QVector", "QVector >", + "QVector >", "QVector") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", + "KDateTime::Spec", "VObject", "QLatin1String", "MailTransport::MessageQueueJob", + "KIdentityManagement::Identity", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate") + + +def module_fix_mapped_types_contact(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QVector", "QVector", + "QVector") + rule_helpers.module_add_classes(filename, sip, rule, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", + "Akonadi::AbstractContactEditorWidget", "KLineEdit", "KLocalizedString") + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types_debug(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "KConfigGroup", "KCoreConfigSkeleton", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def module_fix_mapped_types_kmime(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QVector", "QVector", + "QSet") + rule_helpers.module_add_classes(filename, sip, rule, "Akonadi::SpecialMailCollectionsPrivate", + "KLocalizedString", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate") + rule_helpers.module_add_imports(filename, sip, rule, "KMime/KMime/KMimemod.sip") + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types_notes(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer", "QMap") + rule_helpers.module_add_classes(filename, sip, entry, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", + "KConfigGroup", "KCoreConfigSkeleton") + rule_helpers.module_add_imports(filename, sip, entry, "KMime/KMime/KMimemod.sip") + + +def module_fix_mapped_types_private(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "DataStream", "Akonadi::Protocol::DebugBlock") + + +def module_fix_mapped_types_pim(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KConfigGroup", "KCoreConfigSkeleton", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def module_fix_mapped_types_socialutils(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "KConfigGroup", "KCoreConfigSkeleton", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def module_fix_mapped_types_widgets(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QVector", + "QVector", "QVector", "QVector") + rule_helpers.module_add_classes(filename, sip, entry, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def module_fix_mapped_types_xml(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QVector", "QVector", + "QVector") + rule_helpers.module_add_classes(filename, sip, rule, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", + "KConfigGroup", "KCoreConfigSkeleton") + rule_helpers.module_add_includes(filename, sip, rule, "") + + +_akonadi_qobject_ctscc = """ +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentActionManager; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_AgentBase; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_PreprocessorBase; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ResourceBase; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentFactoryBase; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_Control; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityTreeViewStateSaver; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_Monitor; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ChangeRecorder; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ServerManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_Session; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_SpecialCollections; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_SpecialMailCollections; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_StandardActionManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_StandardMailActionManager; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_ResourceBaseSettings; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ResourceSettings; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentInstanceCreateJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionAttributesSynchronizationJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_PartFetcher; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_RecursiveItemFetchJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ResourceSynchronizationJob; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_Job; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionCopyJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionCreateJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionDeleteJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionFetchJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionModifyJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionMoveJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionStatisticsJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemCopyJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemCreateJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemDeleteJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemFetchJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemModifyJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemMoveJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemSearchJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemSync; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_LinkJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_SearchCreateJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_TransactionBeginJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_TransactionCommitJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_TransactionRollbackJob; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_TransactionSequence; + if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_SpecialCollectionsRequestJob; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_SpecialMailCollectionsRequestJob; + } + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_TrashJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_TrashRestoreJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_UnlinkJob; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ETMViewStateSaver; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionStatisticsDelegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentInstanceModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentTypeModel; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_CollectionModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionStatisticsModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityTreeModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_MessageThreaderProxyModel; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_SelectionProxyModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_FavoriteCollectionsModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentFilterProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionFilterProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityMimeTypeFilterModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityOrderProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_StatisticsProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityRightsFilterModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_RecursiveCollectionFilterProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_TrashFilterProxyModel; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Akonadi_ItemModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_MessageModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentInstanceWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentTypeWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionPropertiesPage; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentTypeDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionPropertiesDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionRequester; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityListView; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionView; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityTreeView; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemView; +%End +""" + + +def container_rules(): + return [ + # + # SIP does not seem to be able to handle empty containers. + # + ["Akonadi::AkonadiCore", "Monitor|Protocol", ".*", ".*", ".*", rule_helpers.container_discard], + # + # We cannot handle templated containers which are this complicated. + # + ["Akonadi::Internal.*", ".*", ".+", ".*", ".*", rule_helpers.container_discard], + # + # Fake classes. + # + ["Akonadi::NoteUtils", "NoteMessageWrapper", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["Akonadi::Protocol", "FetchRelationsCommand|FetchTagsResponse", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["Akonadi", "Scope|ImapSet", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["Akonadi", "ImageProvider", ".*", ".*", ".*", container_add_typedefs_imageprovider], + ["Akonadi", "ImapParser", ".*", ".*", ".*", container_add_typedefs_imapparser], + ["Akonadi", "ExternalPartStorage", ".*", ".*", ".*", rule_helpers.container_make_uncopyable], + ] + + +def forward_declaration_rules(): + return [ + ["standard(contact|mail|calendar)actionmanager.h", "KActionCollection", ".*", rule_helpers.forward_declaration_mark_external], + ["agentactionmanager.h", "KActionCollection|KLocalizedString", ".*", rule_helpers.forward_declaration_mark_external], + ["collectionview.h", "KXMLGUIClient", ".*", rule_helpers.forward_declaration_mark_external], + ] + + +def function_rules(): + return [ + # + # Remove duplicate signatures. + # + ["Akonadi::(Item|Collection)", "parentCollection", ".*", "Akonadi::Collection", ".*", rule_helpers.function_discard], + ["Akonadi::CollectionFetchScope", "ancestorFetchScope", ".*", "Akonadi::CollectionFetchScope", ".*", rule_helpers.function_discard], + ["Akonadi::ItemFetchScope", "tagFetchScope", ".*", "Akonadi::TagFetchScope", ".*", rule_helpers.function_discard], + ["Akonadi::Protocol::FetchItemsCommand", "fetchScope", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["Akonadi::Protocol::FetchCollectionsResponse", "cachePolicy", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + # + # boost templates. + # + ["Akonadi::Item", "setPayloadBaseV2|addPayloadBaseVariant|addToLegacyMappingImpl", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Rewrite using declaration. + # + ["Akonadi::(Collection|Entity(List|Tree)|Item)View", "currentChanged", ".*", ".*", "", _function_rewrite_using_decl], + ["Akonadi::AgentBase::ObserverV2", "collectionChanged", ".*", ".*", "", _function_rewrite_using_decl2], + # + # R-reference. + # + ["Akonadi::Protocol::Command", "Command", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Protocol::Ancestor", "Ancestor", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Protocol::FetchScope", "FetchScope", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Protocol::ScopeContext", "ScopeContext", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Protocol::PartMetaData", "PartMetaData", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Protocol::CachePolicy", "CachePolicy", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Protocol::CachePolicy", "CachePolicy", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Scope", "Scope", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ["Akonadi::Scope::HRID", "HRID", ".*", ".*", ".*&&other", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + # + # Clang seems not to be able to return the tokens for this parameter. + # + ["Akonadi::ImageProvider", "loadImage", "cache", ".*", ".*", _parameter_restore_default], + # + # Akonadi::AbstractDifferencesReporter does not #include , so Clang fills in with ints: fix this up. + # + ["Akonadi::AbstractDifferencesReporter", ".*", "title|name|leftValue|rightValue", ".*", ".*", _parameter_use_qstring], + ] + + +def typedef_rules(): + return [ + # + # SIP thinks there are duplicate signatures. + # + [".*", "QVariantMap", ".*", ".*", rule_helpers.typedef_discard], + # + # Without the needed typedefs, we need to generate QList and QVector by hand. + # + ["Akonadi::Collection", "Id", ".*", ".*", _typedef_add_collections], + # + # We cannot handle templated typedefs which are this complicated. + # + ["Akonadi::Internal.*", ".*", ".*", ".*<.*>.*", rule_helpers.typedef_discard], + ] + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::AgentFilterProxyModel": { #AgentFilterProxyModel : QSortFilterProxyModel + "code": _akonadi_qobject_ctscc + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::ItemSerializerPlugin": { #ItemSerializerPlugin /Abstract/ + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'ItemSerializerPlugin' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_ItemSerializerPluginV2; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::ResourceSettings": { #ResourceSettings : Akonadi::ResourceBaseSettings + "code": _akonadi_qobject_ctscc + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::AgentBase": { #AgentBase : QObject + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'Observer' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AgentBase_ObserverV2; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::SelectionProxyModel": { #SelectionProxyModel : KSelectionProxyModel + "code": _akonadi_qobject_ctscc + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::AddressAttribute": { #AddressAttribute : Akonadi::Attribute + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'Attribute' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AddressAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionQuotaAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityDeletedAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityDisplayAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityHiddenAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_IndexPolicyAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_MessageFolderAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_MessageThreadingAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_PersistentSearchAttribute; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Akonadi::Attribute": { #Attribute /Abstract/ + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'Attribute' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_AddressAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_CollectionQuotaAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityDisplayAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_EntityHiddenAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_MessageFolderAttribute; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Akonadi_MessageThreadingAttribute; + %End + """ + }, + } + + +def modulecode(): + return { + "AkonadiCore/AkonadiCoremod.sip": { + "code": module_fix_mapped_types, + }, + "AkonadiAgentBase/AkonadiAgentBasemod.sip": { + "code": module_fix_mapped_types_agentbase, + }, + "Akonadi/Calendar/Calendarmod.sip": { + "code": module_fix_mapped_types_calendar, + }, + "Akonadi/Contact/Contactmod.sip": { + "code": module_fix_mapped_types_contact, + }, + "AkonadiSearch/Debug/Debugmod.sip": { + "code": module_fix_mapped_types_debug, + }, + "Akonadi/KMime/KMimemod.sip": { + "code": module_fix_mapped_types_kmime, + }, + "Akonadi/Notes/Notesmod.sip": { + "code": module_fix_mapped_types_notes, + }, + "akonadi/private/privatemod.sip": { + "code": module_fix_mapped_types_private, + }, + "AkonadiSearch/PIM/PIMmod.sip": { + "code": module_fix_mapped_types_pim, + }, + "Akonadi/SocialUtils/SocialUtilsmod.sip": { + "code": module_fix_mapped_types_socialutils, + }, + "AkonadiWidgets/AkonadiWidgetsmod.sip": { + "code": module_fix_mapped_types_widgets, + }, + "AkonadiXml/AkonadiXmlmod.sip": { + "code": module_fix_mapped_types_xml, + }, + } Index: find-modules/module_generation/PyKF5/Attica.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Attica.py @@ -0,0 +1,69 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Attica. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def container_fix_typename(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename T::List") + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QList") + rule_helpers.module_add_imports(filename, sip, rule, "Attica/attica/atticamod.sip") + + +def container_rules(): + return [ + # + # SIP cannot handle "typename ...". + # + ["Attica", "ListJob", ".*", ".*", ".*", container_fix_typename], + ] + + +def function_rules(): + return [ + ["Attica::DownloadDescription", "id|type|isDownloadtypLink|hasPrice|category|name|link|distributionType" + "|priceReason|priceAmount|size|gpgFingerprint|gpgSignature|packageName" + "|repository", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["Attica::Provider", "hasCredentials", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["Attica::Provider", "voteForContent", ".*", ".*", ".*bool positiveVote", rule_helpers.function_discard], + ] + + +def typedef_rules(): + return [ + # + # SIP thinks there are duplicate signatures. + # + ["postjob.h", "StringMap", ".*", ".*", rule_helpers.typedef_discard], + ] + + +def modulecode(): + return { + "Attica/Attica/Atticamod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/Baloo.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Baloo.py @@ -0,0 +1,37 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.BalooWidgets. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KService", "KIO::ClipboardUpdater") + + +def modulecode(): + return { + "BalooWidgets/Baloo/Baloomod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/BluezQt.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/BluezQt.py @@ -0,0 +1,45 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.BluezQt. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "QtDBus/QtDBusmod.sip") + + +def container_rules(): + return [ + # + # SIP does not seem to be able to handle these type specialization, but we can live without them? + # + ["BluezQt", "Request", "", ".*", ".*", rule_helpers.container_discard], + ] + + +def modulecode(): + return { + "BluezQt/BluezQt/BluezQtmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/CMakeLists.txt =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/CMakeLists.txt @@ -0,0 +1,410 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# This is a helper script used to locate binaries, libraries and other +# platform-dependent items for use by the Python code. The output is +# NOT CMakeCache.txt et. al. but a JSON file: +# +# { +# "CXX_COMPILE_OPTIONS": "-std=gnu++14", +# "CXX_DEPENDENCIES": { +# "Qt5Core": { +# "COMPILE_FLAGS": "-DQT_CORE_LIB;-DQT_NO_DEBUG;-fPIC", +# "INCLUDE_DIRS": "/usr/include/x86_64-linux-gnu/qt5/;...", +# "LIBRARIES": "/usr/lib/x86_64-linux-gnu/libQt5Core.so.5.7.1" +# }, +# "$": null +# }, +# "CXX_SOURCES": { +# "KF5Activities": { +# "COMPILE_FLAGS": "", +# "INCLUDE_DIRS": "/usr/include/KF5/KActivities;/usr/include/KF5", +# "LIBRARIES": "/usr/lib/x86_64-linux-gnu/libKF5Activities.so.5.31.0" +# }, +# ... +# "$": null +# }, +# "CXX_SOURCE_ROOT": "/usr/include/KF5", +# "SIP_DEPENDENCIES": "/usr/share/sip/PyQt5", +# "SIP_PACKAGE": "PyKF5", +# "$": null +# } +# +# The "$" dict entries are a convenience which the caller discards. +# + +cmake_minimum_required(VERSION 3.7) + +project(PyKF5) + +set(CXX_SOURCES + KF5Activities + KF5ActivitiesStats + KF5Akonadi + KF5AkonadiCalendar + KF5AkonadiContact + KF5AkonadiMime + KF5AkonadiNotes + KF5AkonadiSearch + KF5AkonadiSocialUtils + KF5AlarmCalendar + KF5Archive + KF5Attica + KF5Auth + KF5BalooWidgets + KF5Blog + KF5BluezQt + KF5Bookmarks + KF5CalendarCore + KF5CalendarSupport + KF5CalendarUtils + KF5Codecs + KF5Completion + KF5ComposerEditorNG + KF5Config + KF5ConfigWidgets + KF5Contacts + KF5CoreAddons + KF5Crash + KF5DBusAddons + KF5Declarative + KF5DesignerPlugin + KF5DNSSD + KF5DocTools + KF5Emoticons + KF5EventViews + KF5FileMetaData + KF5FollowupReminder + KF5FrameworkIntegration + KF5GAPI + KF5GlobalAccel + KF5Gpgmepp + KF5GrantleeTheme + KF5Gravatar + KF5GuiAddons + KF5Holidays + KF5I18n + KF5IconThemes + KF5IdentityManagement + KF5IdleTime + KF5IMAP + KF5IncidenceEditor + KF5Init + KF5ItemModels + KF5ItemViews + KF5JobWidgets + KF5JS + KF5JsEmbed + KF5KaddressbookGrantlee + KF5KCMUtils + KF5KDcraw +# KF5KDE4Support + KF5KDEGames +# KF5KDELibs4Support + KF5KdepimDBusInterfaces + KF5KDGantt2 + KF5KExiv2 + KF5KFace + KF5KGeoMap + KF5KHtml + KF5KIO + KF5Kipi + KF5KMahjongglib + KF5KontactInterface + KF5Kross + KF5Ldap + KF5Libkdepim + KF5Libkleo + KF5LibKSieve + KF5MailCommon + KF5MailImporter + KF5MailTransport + KF5Mbox + KF5MediaPlayer + KF5MessageComposer + KF5MessageCore + KF5MessageList + KF5MessageViewer + KF5Mime + KF5NetworkManagerQt + KF5NewStuff + KF5NewStuffCore + KF5NewStuffQuick + KF5Notifications + KF5NotifyConfig + KF5Package + KF5Parts + KF5People + KF5PimCommon + KF5PimTextEdit + KF5Plasma + KF5PlasmaQuick + KF5Plotting + KF5Prison + KF5Pty + KF5Runner + KF5Sane + KF5Screen + KF5SendLater + KF5Service + KF5Solid + KF5Sonnet + KF5Su + KF5Syndication + KF5TemplateParser + KF5TextEditor + KF5TextWidgets + KF5ThreadWeaver + KF5Tnef + KF5UnitConversion + KF5Wallet + KF5WebKit + KF5WidgetsAddons + KF5WindowSystem + KF5XmlGui + KF5XmlRpcClient) + +set(CXX_QT_DEPENDENCIES + Qt5Core + Qt5Widgets + Qt5Xml + Qt5PrintSupport) + +# +# Find the list of targets for a KDE component. +# +function(get_kf5_targets component) + set(file_glob ${${component}_DIR}/*.cmake) + file(GLOB target_files ${file_glob}) + foreach(f ${target_files}) + file(STRINGS ${f} targets REGEX "^ *add_library\\(.*\\) *") + if(NOT targets STREQUAL "") + foreach(target ${targets}) + string(REGEX REPLACE " *add_library\\(([^ \\)]+).*" "\\1" target ${target}) + if(TARGET ${target}) + set(real_targets ${real_targets} ${target}) + else() + message(STATUS "Ignoring invalid target \"${target}\" for ${component} in ${f}") + endif() + endforeach() + endif() + endforeach() + set(targets "${real_targets}" PARENT_SCOPE) +endfunction(get_kf5_targets) + +# +# Find the list of targets for a Qt component. +# +function(get_qt5_targets component) + string(REPLACE "Qt5" "Qt5::" target ${component}) + if(NOT TARGET ${target}) + message(STATUS "Ignoring invalid target \"${target}\" for ${component}") + set(target "") + endif() + set(targets "${target}" PARENT_SCOPE) +endfunction(get_qt5_targets) + +# +# Fetch a target property, recursing if necessary. +# +function(get_target_property_recursive target property) + set(result) + get_target_property(values ${target} ${property}) + if(values STREQUAL "values-NOTFOUND") + # Skip + # message(STATUS "Warning: Target ${target} has no property ${property}") + else() + foreach(value ${values}) + string(FIND ${value} "$ + # + string(REGEX REPLACE "\\$" "\\1" nested_tgt ${value}) + string(REGEX REPLACE "\\$" "\\2" nested_prop ${value}) + get_target_property_recursive(${nested_tgt} ${nested_prop}) + list(APPEND result ${get_target_property_recursive_result}) + else() + list(APPEND result ${value}) + endif() + endforeach() + endif() + set(get_target_property_recursive_result "${result}" PARENT_SCOPE) +endfunction(get_target_property_recursive) + +# +# Find the includes, libraries etc. for a component. +# +function(get_targets_info dict component targets) + if(targets STREQUAL "") + message(STATUS "Warning: No targets for ${component}") + return() + endif() + # + # Make a combined list of includes, libraries etc. + # + # There is a potential impedence mismatch between the directory-centric + # Pythonic notion of a package, and the possibility that the the multiple + # targets *might* have conflicting options. Luckily, this seems not to be + # a problem in KF5. + # + set(libraries) + set(includes) + set(compile_flags) + foreach(target ${targets}) + if(TARGET ${target}) + get_target_property(tmp ${target} LOCATION) + list(APPEND libraries ${tmp}) + get_target_property_recursive(${target} INTERFACE_INCLUDE_DIRECTORIES) + list(APPEND includes ${get_target_property_recursive_result}) + get_target_property_recursive(${target} INTERFACE_COMPILE_DEFINITIONS) + foreach(definition ${get_target_property_recursive_result}) + if(${definition} MATCHES ".*QT_NO_DEBUG>") + # + # Qt uses the formulation "$<$>:QT_NO_DEBUG>". + # + elseif(${definition} MATCHES "QT_.*_LIB") + # + # Qt uses the formulation "QT_CORE_LIB" even for INTERFACE_COMPILE_FLAGS. + # + else() + list(APPEND compile_flags "-D${definition}") + endif() + endforeach() + get_target_property_recursive(${target} INTERFACE_COMPILE_OPTIONS) + list(APPEND compile_flags ${get_target_property_recursive_result}) + else() + message(STATUS "Warning: Ignoring invalid target \"${target}\" in ${f}") + endif() + endforeach() + # + # De-duplicate and write results. + # + if(DEFINED includes) + list(REMOVE_DUPLICATES includes) + # + # Not sure why the headers seem to include this. + # + list(REMOVE_ITEM includes "/usr/include") + endif() + if(DEFINED compile_flags) + list(REMOVE_DUPLICATES compile_flags) + endif() + file(APPEND "${dict}" "\"${component}\": {\n") + file(APPEND "${dict}" " \"LIBRARIES\": \"${libraries}\",\n") + file(APPEND "${dict}" " \"INCLUDE_DIRS\": \"${includes}\",\n") + file(APPEND "${dict}" " \"COMPILE_FLAGS\": \"${compile_flags}\",\n") + file(APPEND "${dict}" " \"$\": null\n") + file(APPEND "${dict}" "},\n") + set(includes "${includes}" PARENT_SCOPE) +endfunction(get_targets_info) + +# +# Find the includes, libraries etc. for a pkg-config component. +# +function(get_pkgconfig_info dict component) + set(libraries) + set(includes ${${component}_INCLUDEDIR}) + set(compile_flags ${${component}_CFLAGS}) + foreach(tmp ${${component}_LIBRARIES}) + find_library(lib${tmp} NAMES ${tmp} PATHS ${${component}_LIBRARIES}) + list(APPEND libraries ${lib${tmp}}) + endforeach() + file(APPEND "${dict}" "\"${component}\": {\n") + file(APPEND "${dict}" " \"LIBRARIES\": \"${libraries}\",\n") + file(APPEND "${dict}" " \"INCLUDE_DIRS\": \"${includes}\",\n") + file(APPEND "${dict}" " \"COMPILE_FLAGS\": \"${compile_flags}\",\n") + file(APPEND "${dict}" " \"$\": null\n") + file(APPEND "${dict}" "},\n") + set(includes "${includes}" PARENT_SCOPE) +endfunction(get_pkgconfig_info) + +# +# Main code. +# +find_package(ECM REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${ECM_MODULE_PATH}) +find_package(PyQt REQUIRED) +include(FeatureSummary) +include(FindPkgConfig) +# +# Write the values we found as a JSON blob. +# +set(dict ${CMAKE_CURRENT_BINARY_DIR}/configure.json) +message(STATUS "Writing ${dict}") +file(WRITE "${dict}" "{\n") +# +# Sources. +# +file(APPEND "${dict}" "\"CXX_SOURCES\": {\n") +foreach(component ${CXX_SOURCES}) + find_package(${component} REQUIRED) + get_kf5_targets(${component}) + get_targets_info(${dict} ${component} "${targets}") + # + # Find the source root. + # + if(NOT DEFINED CXX_SOURCE_ROOT) + foreach(include ${includes}) + if(include MATCHES ".*/KF5$") + set(CXX_SOURCE_ROOT ${include}) + break() + endif() + endforeach() + endif() +endforeach() +if(NOT DEFINED CXX_SOURCE_ROOT) + message(SEND_ERROR "Unable to find CXX_SOURCE_ROOT") + return() +endif() +# +# Write results. +# +file(APPEND "${dict}" "\"$\": null\n") +file(APPEND "${dict}" "},\n") +# +# Dependencies. +# +file(APPEND "${dict}" "\"CXX_DEPENDENCIES\": {\n") +foreach(component ${CXX_QT_DEPENDENCIES}) + find_package(${component}) + get_qt5_targets(${component}) + get_targets_info(${dict} ${component} "${targets}") +endforeach() +#pkg_search_module(LIBICAL REQUIRED libical) +#get_pkgconfig_info(${dict} LIBICAL) +file(APPEND "${dict}" "\"$\": null\n") +file(APPEND "${dict}" "},\n") +file(APPEND "${dict}" "\"CXX_COMPILE_OPTIONS\": \"-std=gnu++14\",\n") +file(APPEND "${dict}" "\"SIP_DEPENDENCIES\": \"${PyQt_INCLUDE_DIRS}\",\n") +file(APPEND "${dict}" "\"CXX_SOURCE_ROOT\": \"${CXX_SOURCE_ROOT}\",\n") +file(APPEND "${dict}" "\"SIP_PACKAGE\": \"${CMAKE_PROJECT_NAME}\",\n") +file(APPEND "${dict}" "\"$\": null\n") +file(APPEND "${dict}" "}\n") +unset(component) +unset(dict) Index: find-modules/module_generation/PyKF5/CalendarSupport.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/CalendarSupport.py @@ -0,0 +1,48 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.CalendarSupport. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QVector", "QVector", + "QVector", "QVector >", + "QVector >", + "QVector >") + + +def modulecode(): + return { + "CalendarSupport/CalendarSupportmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/FindPyQt.cmake =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/FindPyQt.cmake @@ -0,0 +1,75 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#.rst: +# FindPyQt +# -------- +# +# Find PyQt +# +# This module finds an installed PyQt. It sets the following variables: +# +# :: +# +# PyQt_FOUND - set to true if PyQt is found +# PyQt_DIR - the directory where PyQt is installed +# PyQt_CORE_MODULE - the PyQt QtCoremod.sip file +# PyQt_VERSION - the version number of the PyQt installation +# +# +# +# The minimum required version of PyQt can be specified using the +# standard syntax, e.g. find_package(PyQt 4.19) +# +# All information is collected from the PyQt_CORE_MODULE so the version +# to be found can be changed from the command line by means of setting +# PyQt_CORE_MODULE. + +find_file(PyQt_CORE_MODULE QtCoremod.sip /usr/share/sip/PyQt5/QtCore) + +if(PyQt_CORE_MODULE) + file(STRINGS ${PyQt_CORE_MODULE} timeline REGEX "%Timeline[^{]*{[^}]+}" NEWLINE_CONSUME) + if(timeline STREQUAL "") + message(SEND_ERROR "No %Timeline in \"${PyQt_CORE_MODULE}\"") + else() + string(REGEX REPLACE ".*%Timeline[^{]*{([^}]+)}.*" "\\1" timeline ${timeline}) + string(REGEX REPLACE "[\n\r]+" "" timeline ${timeline}) + string(REGEX REPLACE " +" " " timeline ${timeline}) + separate_arguments(timeline) + foreach(tag ${timeline}) + # Skip to the last tag, and extract the version number. + string(REGEX REPLACE "Qt_([0-9]+)_([0-9]+)_([0-9]+)" "\\1.\\2.\\3" tag ${tag}) + set(PyQt_VERSION ${tag} CACHE STRING "PyQt version" FORCE) + endforeach() + get_filename_component(PyQt_INCLUDE_DIRS ${PyQt_CORE_MODULE} DIRECTORY) + get_filename_component(PyQt_INCLUDE_DIRS ${PyQt_INCLUDE_DIRS} DIRECTORY) + endif() +endif() + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(PyQt REQUIRED_VARS PyQt_INCLUDE_DIRS + VERSION_VAR PyQt_VERSION) + +mark_as_advanced(PyQt_VERSION) Index: find-modules/module_generation/PyKF5/FollowupReminder.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/FollowupReminder.py @@ -0,0 +1,39 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.FollowupReminder. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QExplicitlySharedDataPointer", "QList") + rule_helpers.module_add_classes(filename, sip, entry, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + +def modulecode(): + return { + "FollowupReminder/FollowupRemindermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/IncidenceEditor.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/IncidenceEditor.py @@ -0,0 +1,42 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.IncidenceEditor. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSet", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QVector >") + + +def modulecode(): + return { + "IncidenceEditor/IncidenceEditormod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KActivitiesStats.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KActivitiesStats.py @@ -0,0 +1,79 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KActivitiesStats. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def _delete_duplicate_content(filename, sip, rule): + sip["decl"] = "" + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QHash") + rule_helpers.module_add_classes(filename, sip, rule, "std::random_access_iterator_tag") + #rule_helpers.module_add_includes(filename, sip, rule, "") + + +def function_rules(): + return [ + # + # Remove unsupported signature. + # + ["KActivities::Stats::Query", "Query", ".*", ".*", ".*&&.*", rule_helpers.function_discard], + ["KActivities::Stats::ResultSet", "ResultSet", ".*", ".*", ".*&&.*", rule_helpers.function_discard], + ["KActivities::Stats::ResultSet::Result", "Result", ".*", ".*", ".*&&.*", rule_helpers.function_discard], + ["KActivities::Stats::ResultSet::const_iterator", "operator\*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KActivities::Stats::ResultSet::const_iterator", "operator->", ".*", ".*", ".*", rule_helpers.function_discard], + ["KActivities::Stats::ResultSet::const_iterator", "operator(-|\+).*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KActivities::Stats::ResultSet::const_iterator", "operator\[\]", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KActivitiesStats/KActivities/Stats/Statsmod.sip": { + "code": module_fix_mapped_types, + }, + "KActivitiesStats/kactivitiesstats/cleaning.h": { + "code": _delete_duplicate_content, + }, + "KActivitiesStats/kactivitiesstats/query.h": { + "code": _delete_duplicate_content, + }, + "KActivitiesStats/kactivitiesstats/resultmodel.h": { + "code": _delete_duplicate_content, + }, + "KActivitiesStats/kactivitiesstats/resultset.h": { + "code": _delete_duplicate_content, + }, + "KActivitiesStats/kactivitiesstats/resultwatcher.h": { + "code": _delete_duplicate_content, + }, + "KActivitiesStats/kactivitiesstats/terms.h": { + "code": _delete_duplicate_content, + }, + } Index: find-modules/module_generation/PyKF5/KAlarmCal.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KAlarmCal.py @@ -0,0 +1,55 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KAlarmCal. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QVector >", "QVector", + "QList", "QList", + "QMap") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", "VObject", "QLatin1String", + "KConfigGroup", "KCoreConfigSkeleton", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate", "KAlarmCal::CalEvent::Type", "KDateTime::SpecType", + "KDateTime::Comparison", "KHolidays::HolidayRegion") + + +def function_rules(): + return [ + ["KAlarmCal::Repetition", "operator(!| bool)", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KAlarmCal/KAlarmCal/KAlarmCalmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KArchive.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KArchive.py @@ -0,0 +1,111 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KArchive. This modules describes: + + * Supplementary SIP file generator rules. +""" + +def container_add_includes(container, sip, rule): + sip["code"] += """%TypeHeaderCode +#include +#include +#include +%End +""" + + +def function_rewrite_dir(container, fn, sip, rule): + sip["parameters"][0] = "KArchive *archive" + + +def function_rewrite_entry(container, fn, sip, rule): + sip["parameters"] = ["KArchive *archive", "const QString &name", "int access", "const QDateTime &date", + "const QString &user", "const QString &group", "const QString &symlink"] + + +def function_rewrite_file(container, fn, sip, rule): + sip["parameters"] = ["KArchive *archive", "const QString &name", "int access", "const QDateTime &date", + "const QString &user", "const QString &group", "const QString &symlink", "qint64 pos", + "qint64 size"] + + +def function_rewrite_setsize(container, fn, sip, rule): + sip["parameters"] = ["qint64 s"] + + +def function_rewrite_copyto(container, fn, sip, rule): + sip["parameters"] = ["const QString &dest"] + + +def function_return_karchive(container, fn, sip, rule): + sip["fn_result"] = "KArchive *" + + +def function_return_qdatetime(container, fn, sip, rule): + sip["fn_result"] = "QDateTime" + + +def function_return_qstring(container, fn, sip, rule): + sip["fn_result"] = "QString" + + +def function_return_mode_t(container, fn, sip, rule): + sip["fn_result"] = "int" + + +def function_return_qint64(container, fn, sip, rule): + sip["fn_result"] = "qint64" + + +def function_return_qbytearray(container, fn, sip, rule): + sip["fn_result"] = "virtual QByteArray" + + +def function_return_qiodevice(container, fn, sip, rule): + sip["fn_result"] = "virtual QIODevice *" + + +def container_rules(): + return [ + # + # Completely rewrite args because ofmissing fwd decls/#includes. + # + [".*", "KArchiveEntry|KArchiveDirectory|KArchiveFile", ".*", ".*", ".*", container_add_includes], + ] + + +def function_rules(): + return [ + # + # Completely rewrite args because ofmissing fwd decls/#includes. + # + ["KArchiveEntry", "name|user|group|symLinkTarget", ".*", ".*", ".*", function_return_qstring], + ["KArchiveEntry", "date", ".*", ".*", ".*", function_return_qdatetime], + ["KArchiveEntry", "archive", ".*", ".*", ".*", function_return_karchive], + ["KArchiveEntry", "permissions", ".*", ".*", ".*", function_return_mode_t], + ["KArchiveEntry", "KArchiveEntry", ".*", ".*", ".*", function_rewrite_entry], + ["KArchiveDirectory", "KArchiveDirectory", ".*", ".*", ".*", function_rewrite_dir], + ["KArchiveFile", "KArchiveFile", ".*", ".*", ".*", function_rewrite_file], + ["KArchiveFile", "position|size", ".*", ".*", ".*", function_return_qint64], + ["KArchiveFile", "setSize", ".*", ".*", ".*", function_rewrite_setsize], + ["KArchiveFile", "copyTo", ".*", ".*", ".*", function_rewrite_copyto], + ["KArchiveFile", "data", ".*", ".*", ".*", function_return_qbytearray], + ["KArchiveFile", "createDevice", ".*", ".*", ".*", function_return_qiodevice], + ] Index: find-modules/module_generation/PyKF5/KAuth.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KAuth.py @@ -0,0 +1,50 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KAuth. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def function_rules(): + return [ + # + # SIP thinks there are duplicate signatures. + # + ["KAuth::ActionReply", "ActionReply", ".*", ".*", ".*int.*", rule_helpers.function_discard], + ] + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QMap") + rule_helpers.module_add_imports(filename, sip, entry, "QtWidgets/QtWidgetsmod.sip") + + +def modulecode(): + return { + "KAuth/KAuthmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KBlog.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KBlog.py @@ -0,0 +1,43 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KBlog. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", "VObject", "QLatin1String") + + +def modulecode(): + return { + "KBlog/KBlog/KBlogmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KBookmarks.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KBookmarks.py @@ -0,0 +1,104 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KBookmarks. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_imports(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "QtDBus/QtDBusmod.sip", "KXmlGui/KXmlGuimod.sip") + + +def _variable_void(container, variable, sip, matcher): + sip["decl"] = "void *" + + +def container_rules(): + return [ + # + # No multiple inheritance. + # + ["kbookmarkimporter.h", "KXBELBookmarkImporterImpl", ".*", ".*", ".*", rule_helpers.container_discard], + # + # Protected. + # + ["konqbookmarkmenu.h", "KonqBookmarkMenu::DynMenuInfo", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + # + # Protected. + # + ["KonqBookmarkMenu", "showDynamicBookmarks|setDynamicBookmarks", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def variable_rules(): + return [ + ["KonqBookmarkMenu::DynMenuInfo", "d", ".*DynMenuInfoPrivate.*", _variable_void], + ] + + +def modulecode(): + return { + "KBookmarks/KBookmarksmod.sip": { + "code": module_fix_imports, + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kbookmarkowner.h::KBookmarkOwner": { + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KBookmarkOwner' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KonqBookmarkOwner; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kbookmarkexporter.h::KBookmarkExporterBase": { + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KBookmarkExporterBase' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIEBookmarkExporterImpl; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNSBookmarkExporterImpl; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KOperaBookmarkExporterImpl; + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/KCMUtils.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCMUtils.py @@ -0,0 +1,97 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCMUtils. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QExplicitlySharedDataPointer", + "QExplicitlySharedDataPointer", "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", "VObject", "QLatin1String") + + +def module_fix_mapped_types_ksettings(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", + "QExplicitlySharedDataPointer") + + +def container_rules(): + return [ + ["kcmoduleinfo.h", "KCModuleInfo", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def modulecode(): + return { + "KCMUtils/KCMUtilsmod.sip": { + "code": module_fix_mapped_types, + }, + "KCMUtils/ksettings/ksettingsmod.sip": { + "code": module_fix_mapped_types_ksettings, + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kcmoduleproxy.h::KCModuleProxy": { + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KEmoticons; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KEmoticonsProvider; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIdleTime; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSettings_PluginPage; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCModuleProxy; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPluginSelector; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KCMultiDialog; + if (dynamic_cast(sipCpp)) + sipType = sipType_KSettings_Dialog; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPrintPreview; + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/KCalCore.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCalCore.py @@ -0,0 +1,90 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCalCore. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import os + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, entry, "QMap") + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", + # Uncommenting this causes SIP to crash. + # "VObject", + "QLatin1String") + + +def typedef_duplicate_discard(container, typedef, sip, matcher): + """ + There are multiple definitions like this: + + typedef KCalCore::SortableList DateTimeList; + + We need to get rid of the second one. + """ + filename = os.path.basename(container.translation_unit.spelling) + if filename != "incidencebase.h": + rule_helpers.typedef_discard(container, typedef, sip, matcher) + + +def container_rules(): + return [ + # + # Duplicate Akonadi::SuperClass. + # + ["(event|journal|todo).h", "Akonadi", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + # + # Delete non-const. + # + ["KCalCore::Attendee", "customProperties", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + # + # SIP needs help with the semantics of KCalCore::SortableList<>. + # + ["KCalCore::Recurrence", "set(R|Ex)Date(|Time)s", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def typedef_rules(): + return [ + ["KCalCore", "Date(Time|)List", ".*", ".*", typedef_duplicate_discard], + ] + + +def modulecode(): + return { + "KCalCore/KCalCore/KCalCoremod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KCalUtils.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCalUtils.py @@ -0,0 +1,54 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCalUtils. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QVector >") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", "VObject", "QLatin1String", + "QDropEvent", "QDrag", "QWidget", "KCalUtils::HTMLExportSettings", "KGuiItem") + + +def container_rules(): + return [ + ["KCalUtils", "DndFactory", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def modulecode(): + return { + "KCalUtils/KCalUtils/KCalUtilsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KCodecs.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCodecs.py @@ -0,0 +1,53 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCodecs. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + +def _mark_abstract(container, sip, matcher): + sip["annotations"].add("Abstract") + + +def container_rules(): + return [ + ["KCodecs", ".*", ".*", ".*", ".*", _mark_abstract], + ] + + +def function_rules(): + return [ + ["KCodecs::Codec", "encode", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + ["KCodecs::Codec", "decode", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + + ["KCodecs::Encoder", "encode", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + ["KCodecs::Encoder", "finish", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + ["KCodecs::Encoder", "write", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + ["KCodecs::Encoder", "writeCRLF", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + ["KCodecs::Encoder", "flushOutputBuffer", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + + ["KCodecs::Decoder", "decode", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + ["KCodecs::Decoder", "finish", ".*", ".*", ".*char.*&.*", rule_helpers.function_discard], + + ["KCharsets", "codecForName", ".*", ".*", ".*name", rule_helpers.function_discard], + ["KCharsets", "fromEntity", ".*", ".*", ".*str", rule_helpers.function_discard], + ] Index: find-modules/module_generation/PyKF5/KCompletion.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCompletion.py @@ -0,0 +1,70 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCompletion. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def _function_rewrite_using_decl(container, function, sip, matcher): + sip["parameters"] = ["int index", "const QStringList &texts"] + + +def parameter_rewrite_template(container, function, parameter, sip, matcher): + if sip["name"] == "i": + sip["decl"] = "Key i" + elif sip["name"] == "t": + sip["decl"] = "const T &t" + else: + assert False, "Unexpected parameter {}".format(sip["name"]) + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, entry, "QList") + + +def function_rules(): + return [ + ["KCompletionBase", "keyBindingMap|getKeyBindings|setKeyBindingMap", ".*", ".*", ".*", rule_helpers.function_discard], + ["KCompletionMatches", "KCompletionMatches", ".*", ".*", ".*KCompletionMatchesWrapper.*", rule_helpers.function_discard], + # + # Rewrite using declaration. + # + ["KHistoryComboBox", "insertItems", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def parameter_rules(): + return [ + ["KSortableList", ".*", "i|t", ".*", ".*", parameter_rewrite_template], + ] + + +def modulecode(): + return { + "KCompletion/KCompletionmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KConfigCore.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KConfigCore.py @@ -0,0 +1,368 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KConfigCore. This modules describes: + + * Supplementary SIP file generator rules. +""" + +from copy import deepcopy + +import rule_helpers +import rules_engine + + +def _mark_abstract_and_discard_QSharedData(container, sip, matcher): + rule_helpers.container_mark_abstract(container, sip, matcher) + rule_helpers.container_discard_QSharedData_base(container, sip, matcher) + + +def _function_fixup_template_params(container, function, sip, matcher): + sip["parameters"] = ["const T &v"] + + +def _function_rewrite_using_decl(container, function, sip, matcher): + flags = "QFlags flags = KConfigBase::Normal" + sip["parameters"] = ["const QByteArray & group", flags] + sip["code"] = """ void deleteGroup(const QString &group, {}); + void deleteGroup(const char *group, {}); +""".format(flags, flags) + + +def _discard_non_const_suffixes(container, function, sip, matcher): + rule_helpers.function_discard(container, function, sip, matcher) + + +def parameter_add_brackets(container, function, parameter, sip, matcher): + sip["init"] += "()" + + +def _kcoreconfigskeleton_add_item_xxx(function, sip, entry): + sip["code"] = """ +%MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = new PyItem{}(sipCpp->currentGroup(), a3->isNull() ? *a0 : *a3, a1, a2); + sipCpp->addItem(sipRes, *a0); + Py_END_ALLOW_THREADS +%End +""".format(sip["ctx"]) + + +def _kcoreconfigskeleton_item_xxx(function, sip, entry): + sip["cxx_parameters"] = deepcopy(sip["parameters"]) + sip["cxx_fn_result"] = "" + sip["code"] = """ +%MethodCode + Py_BEGIN_ALLOW_THREADS + sipCpp = (sipKCoreConfigSkeleton_Item{} *)(new KCoreConfigSkeleton::Item{}(*a0, *a1, a2, a3)); + Py_END_ALLOW_THREADS +%End +""".replace("{}", sip["ctx"]) + sip["parameters"][2] = sip["parameters"][2].replace("&", "") + + +def _kcoreconfigskeleton_item_enum(function, sip, entry): + sip["cxx_parameters"] = deepcopy(sip["parameters"]) + sip["cxx_fn_result"] = "" + sip["code"] = """ +%MethodCode + Py_BEGIN_ALLOW_THREADS + sipCpp = (sipKCoreConfigSkeleton_Item{} *)(new KCoreConfigSkeleton::Item{}(*a0, *a1, a2, *a3, a4)); + Py_END_ALLOW_THREADS +%End +""".replace("{}", sip["ctx"]) + sip["parameters"][2] = sip["parameters"][2].replace("&", "") + + +def _kcoreconfigskeleton_item_add_py_subclass(filename, sip, entry): + result = """ +%ModuleHeaderCode +#include +""" + for ctx in ({"Type": "Bool", "cpptype": "bool", "defaultValue": 1}, + {"Type": "Int", "cpptype": "qint32", "defaultValue": 1}, + {"Type": "UInt", "cpptype": "quint32", "defaultValue": 1}, + {"Type": "LongLong", "cpptype": "qint64", "defaultValue": 1}, + {"Type": "ULongLong", "cpptype": "quint64", "defaultValue": 1}, + {"Type": "Double", "cpptype": "double", "defaultValue": 1}, + ): + result += """ +class PyItem{Type} : public KCoreConfigSkeleton::Item{Type} +{{ +public: + PyItem{Type} (const QString &group, const QString &key, {cpptype}& val, {cpptype} defaultValue = {defaultValue}) : + KCoreConfigSkeleton::Item{Type} (group, key, this->value, defaultValue), + value(val) + {{ + }} + +private: + {cpptype} value; +}}; +""".format(**ctx) + + result += """ +class PyItemEnum : public KCoreConfigSkeleton::ItemEnum +{ +public: + PyItemEnum (const QString& group, const QString& key, int& val, const QList& choices, int defaultValue = 0) : + KCoreConfigSkeleton::ItemEnum(group, key, this->value, choices, defaultValue), + value(val) + { + }; + +private: + int value; +}; +%End\n +""" + sip["code"] = result + + +def module_fix_includes(filename, sip, rule): + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, entry, "QExplicitlySharedDataPointer", + "QList", "QList") + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList", "QMap") + + +def container_rules(): + return [ + ["kconfigbackend.h", "KConfigBackend", ".*", ".*", ".*", _mark_abstract_and_discard_QSharedData], + ["kconfigbase.h", "KConfigBase", ".*", ".*", ".*", rule_helpers.container_mark_abstract], + ["ksharedconfig.h", "KSharedConfig", ".*", ".*", ".*QSharedData.*", rule_helpers.container_discard_QSharedData_base], + # + # Emit templated containers. + # + ["kcoreconfigskeleton.h", "KConfigSkeletonGenericItem", ".*", ".*", ".*", rule_helpers.noop], + ] + + +def function_rules(): + return [ + ["KCoreConfigSkeleton|KConfig.*|KDesktopFile|KSharedConfig", "groupImpl|group|config|actionGroup", ".*", ".*", ".*", ".*", "(?! const).*", _discard_non_const_suffixes], + ["KConfigGroup", "KConfigGroup", ".*", ".*", "KConfigBase.*", ".*", "(?! const)", _discard_non_const_suffixes], + ["KConfigSkeletonGenericItem", "value", ".*", ".*", ".*", "inline ", "", rule_helpers.function_discard], + ["KConfigSkeletonGenericItem", "setValue|setDefaultValue", ".*", ".*", ".*", _function_fixup_template_params], + # + # What *is* KEntryMap? + # + ["KConfigBackend", ".*", ".*", ".*", ".*KEntryMap.*", rule_helpers.function_discard], + ["KConfigBackend", "create", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Rewrite using declaration. + # + ["KConfigGroup", "deleteGroup", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def typedef_rules(): + return [ + # + # It is not clear how to represent "typedef QHash < QString, KConfigSkeletonItem * >::Iterator DictIterator;" + # in SIP. Discard it. + # + ["KConfigSkeletonItem", "DictIterator", ".*", ".*", rule_helpers.typedef_discard], + ] + + +def modulecode(): + return { + "KConfigCore/KConfigCoremod.sip": { + "code": module_fix_mapped_types, + }, + "KConfigCore/KConfig": { + "code": module_fix_includes, + }, + "KConfigCore/KCoreConfigSkeleton": { + "code": _kcoreconfigskeleton_item_add_py_subclass + }, + } + + +def methodcode(): + return { + "KCoreConfigSkeleton": + { + "addItemBool": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "Bool", + }, + "addItemInt": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "Int", + }, + "addItemUInt": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "UInt", + }, + "addItemLongLong": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "LongLong", + }, + "addItemInt64": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "LongLong", + }, + "addItemULongLong": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "ULongLong", + }, + "addItemUInt64": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "ULongLong", + }, + "addItemDouble": + { + "code": _kcoreconfigskeleton_add_item_xxx, + "ctx": "Double", + }, + }, + "KCoreConfigSkeleton::ItemBool": + { + "ItemBool": + { + "code": _kcoreconfigskeleton_item_xxx, + "ctx": "Bool", + }, + }, + "KCoreConfigSkeleton::ItemInt": + { + "ItemInt": + { + "code": _kcoreconfigskeleton_item_xxx, + "ctx": "Int", + }, + }, + "KCoreConfigSkeleton::ItemLongLong": + { + "ItemLongLong": + { + "code": _kcoreconfigskeleton_item_xxx, + "ctx": "LongLong", + }, + }, + "KCoreConfigSkeleton::ItemEnum": + { + "ItemEnum": + { + "code": _kcoreconfigskeleton_item_enum, + "ctx": "Enum", + }, + }, + "KCoreConfigSkeleton::ItemUInt": + { + "ItemUInt": + { + "code": _kcoreconfigskeleton_item_xxx, + "ctx": "UInt", + }, + }, + "KCoreConfigSkeleton::ItemULongLong": + { + "ItemULongLong": + { + "code": _kcoreconfigskeleton_item_xxx, + "ctx": "ULongLong", + }, + }, + "KCoreConfigSkeleton::ItemDouble": + { + "ItemDouble": + { + "code": _kcoreconfigskeleton_item_xxx, + "ctx": "Double", + }, + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kcoreconfigskeleton.h::KCoreConfigSkeleton": { + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KConfigSkeletonItem' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemBool; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemDateTime; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemDouble; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KCoreConfigSkeleton_ItemInt; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemEnum; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemIntList; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemLongLong; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemPoint; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemProperty; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemRect; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemSize; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KCoreConfigSkeleton_ItemString; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemPassword; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemPath; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KCoreConfigSkeleton_ItemStringList; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemPathList; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemUInt; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemULongLong; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemUrl; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton_ItemUrlList; + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/KConfigGui.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KConfigGui.py @@ -0,0 +1,46 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KConfigGui. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_includes(filename, sip, rule): + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, entry, "QExplicitlySharedDataPointer") + + +def modulecode(): + return { + "KConfigGui/KConfigGuimod.sip": { + "code": module_fix_mapped_types, + }, + "KConfigGui/KConfigSkeleton": { + "code": module_fix_includes, + }, + } Index: find-modules/module_generation/PyKF5/KConfigWidgets.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KConfigWidgets.py @@ -0,0 +1,80 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KConfigWidgets. This modules describes: + + * Supplementary SIP file generator rules. +""" + + +import rule_helpers +import rules_engine + + +def _function_rewrite_using_decl(container, function, sip, matcher): + sip["parameters"] = ["QAction* action"] + sip["code"] = """ //void triggered(int index); + void triggered(const QString &text); +""" + + +def _delete_duplicate_content(filename, sip, entry): + sip["decl"] = "" + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList") + + +def container_rules(): + return [ + ["kcolorscheme.h", "KStatefulBrush", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + # + # Rewrite using declaration. + # + ["KCodecAction", "triggered", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def parameter_rules(): + return [ + # + # Override the default "parent" rule. + # + ["KStandardAction", ".*", "parent", ".*", ".*", rule_helpers.noop] + ] + + +def modulecode(): + return { + "KConfigWidgets/ktip.h": { + "code": _delete_duplicate_content + }, + "KConfigWidgets/KConfigWidgetsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KContacts.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KContacts.py @@ -0,0 +1,58 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KContacts. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def _container_discard_templated_bases_and_fake(container, sip, matcher): + sip["base_specifiers"] = [b for b in sip["base_specifiers"] if "<" not in b] + rule_helpers.container_fake_derived_class(container, sip, matcher) + + +def container_rules(): + return [ + ["KContacts", "Address", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["KContacts", "AddresseeList", ".*", ".*", ".*", _container_discard_templated_bases_and_fake], + ["KContacts", "PhoneNumber", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + ["KContacts::ContactGroup", "contact.*Reference|data", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KContacts::Field", "Field", ".*", ".*", ".*Private.*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KContacts/KContacts/KContactsmod.sip": { + "code": + """ + %If (!KContacts_KContacts_KContactsmod) + class KConfigGroup /External/; + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/KCoreAddons.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCoreAddons.py @@ -0,0 +1,173 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCoreAddons. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def function_discard_impl(container, function, sip, matcher): + if function.extent.start.column == 1: + sip["name"] = "" + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QMap") + rule_helpers.modulecode_make_local(filename, sip, entry, "QList", "QList") + + +def container_rules(): + return [ + ["KPluginFactory", "InheritanceChecker", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + # + # Strip protected functions which require private stuff to work. + # + ["KPluginFactory", "KPluginFactory", ".*", ".*", "KPluginFactoryPrivate.*", rule_helpers.function_discard], + ["KJob", ".*", ".*", ".*", ".*KJob::QPrivateSignal.*", rule_helpers.function_discard], + ["KCompositeJob", "KCompositeJob", ".*", ".*", "KCompositeJobPrivate.*", rule_helpers.function_discard], + ["KUser", "KUser", ".*", ".*", ".*passwd.*", rule_helpers.function_discard], + ["KUserGroup", "KUserGroup", ".*", ".*", ".*group.*", rule_helpers.function_discard], + # + # Use forward declared types. + # + ["KPluginFactory", "createPartObject", ".*", ".*", ".*", rule_helpers.function_discard], + ["KPluginFactory", "create", ".*", ".*", ".*", rule_helpers.function_discard], + ["KMacroExpanderBase", "expandMacrosShellQuote", ".*", ".*", "QString &str", rule_helpers.function_discard], + # + # This class has inline implementations in the header file. + # + ["KPluginName", ".*", ".*", ".*", ".*", function_discard_impl], + # + # SIP cannot handle std::function. + # + ["KPluginLoader", "instantiatePlugins|findPlugins|forEachPlugin", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP thinks there are duplicate signatures. + # + ["KRandomSequence", "setSeed", ".*", ".*", "int.*", rule_helpers.function_discard], + # + # kuser.h has inline operators. + # + [".*", "operator!=", ".*", ".*", "const KUser(Group){0,1} &other", rule_helpers.function_discard], + # + # Need typedef for argument, plus custom logic. + # + ["KPluginFactory", "registerPlugin", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP: unsupported signal argument type, unsupported function argument type (QPair) + # + ["KJob|KJobTrackerInterface|KStatusBarJobTracker|KUiServerJobTracker|KWidgetJobTracker", "description", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + ["KShell", "splitArgs", "err", ".*", ".*", rule_helpers.parameter_out], + ] + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kautosavefile.h::KAutoSaveFile": { + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KAuth_ActionWatcher; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KAutostart; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCoreConfigSkeleton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDEDModule; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KJob; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCompositeJob; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KJobTrackerInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KJobUiDelegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLibLoader; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLocalSocketServer; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPluginFactory; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSycoca; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSystemTimeZones; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KToolInvocation; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFilterDev; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPtyDevice; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTcpSocket; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLocalSocket; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KAutoSaveFile; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSaveFile; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTemporaryFile; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KProcess; + if (dynamic_cast(sipCpp)) + sipType = sipType_KPtyProcess; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLibrary; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPluginLoader; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Sonnet_BackgroundChecker; + %End + """ + }, + } + + +def modulecode(): + return { + "KCoreAddons/KCoreAddonsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KCrash.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KCrash.py @@ -0,0 +1,32 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KCrash. This modules describes: + + * Supplementary SIP file generator rules. +""" + +def _parameter_restore_typedef(container, function, parameter, sip, matcher): + sip["decl"] = "KCrash::HandlerType " + sip["name"] + + +def parameter_rules(): + return [ + ["KCrash", "setCrashHandler|setEmergencySaveFunction", ".*", ".*", ".*", _parameter_restore_typedef], + ] Index: find-modules/module_generation/PyKF5/KDEWebKit.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KDEWebKit.py @@ -0,0 +1,43 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KDEWebKit. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QPair") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KService", "KIO::ClipboardUpdater", + "KParts::ReadOnlyPart") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/KIO/KIOmod.sip", "KIOCore/kio/kiomod.sip", + "KIOCore/KIOCoremod.sip") + + +def modulecode(): + return { + "KDEWebKit/KDEWebKitmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KDGantt2.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KDGantt2.py @@ -0,0 +1,49 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KDGantt2. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def forward_declaration_rules(): + return [ + ["KDGantt", "GraphicsScene|ConstraintGraphicsItem", ".*", rule_helpers.noop], + ] + + +def function_rules(): + return [ + ["KDGantt::Constraint", "debug", ".*", ".*", ".*", rule_helpers.function_discard], + ["KDGantt::View", "leftView|splitter|rowController|graphicsView|ganttProxyModel", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ] + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "KDGantt::ConstraintModel", "QPrinter") + + +def modulecode(): + return { + "KDGantt2/KDGantt2mod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KDeclarative.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KDeclarative.py @@ -0,0 +1,47 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KDeclarative. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "KCoreConfigSkeleton") + + +def container_add_typedefs(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "QMultiHash") + + +def container_rules(): + return [ + ["configmodule.h", "QQmlTypeInfo", ".*", ".*", ".*", rule_helpers.container_discard], + ["CalendarEvents", "CalendarEventsPlugin", ".*", ".*", ".*", container_add_typedefs], + ] + + +def modulecode(): + return { + "KDeclarative/KDeclarative/KDeclarativemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KEmoticons.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KEmoticons.py @@ -0,0 +1,41 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KEmoticons. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QExplicitlySharedDataPointer", + "QHash") + + +def modulecode(): + return { + "KEmoticons/KEmoticonsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KExiv2.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KExiv2.py @@ -0,0 +1,36 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KExiv2. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "QMatrix") + + +def modulecode(): + return { + "KExiv2/KExiv2/KExiv2mod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KF5KDEGames.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KF5KDEGames.py @@ -0,0 +1,142 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KF5KDEGames. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers +import rules_engine + + +def module_remove_redundant(filename, sip, matcher): + lines = [] + for l in sip["decl"].split("\n"): + if "KF5KDEGames/KF5KDEGamesmod.sip" in l: + l = "// " + l + lines.append(l) + sip["decl"] = "\n".join(lines) + + +def _function_discard_class(container, function, sip, matcher): + sip["fn_result"] = sip["fn_result"].replace("class ", "") + + +def _function_fully_qualify_parm(container, function, sip, matcher): + sip["parameters"][0] = sip["parameters"][0].replace("FieldInfo", "KScoreDialog::FieldInfo") + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KConfig", "QMatrix") + + +def module_fix_mapped_types_private(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QMap", "QMap", + "QPair") + rule_helpers.module_add_classes(filename, sip, entry, "QMatrix /External/") + + +def forward_declaration_rules(): + return [ + ["(k|)highscore.h|kchatbase.h", "KConfig", ".*", rule_helpers.forward_declaration_mark_external], + ["kgame.h", "KRandomSequence", ".*", rule_helpers.forward_declaration_mark_external], + ["kgamethemeselector.h", "KConfigSkeleton", ".*", rule_helpers.forward_declaration_mark_external], + ["kstandardgameaction.h", "K(RecentFiles|Select|Toggle)Action", ".*", rule_helpers.forward_declaration_mark_external], + ["kg(ame|)difficulty.h", "KXmlGuiWindow", ".*", rule_helpers.forward_declaration_mark_external], + ] + + +def container_rules(): + return [ + ["KGamePropertyBase", "Flags", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + # + # Duplicate. + # + ["kgamerenderer.h", "qHash", ".*", ".*", ".*", rule_helpers.function_discard], + [".*", "GAMES_.*", ".*", "const QLoggingCategory &", ".*", rule_helpers.function_discard], + ["KScoreDialog", "addScore", ".*", ".*", ".*FieldInfo.*", _function_fully_qualify_parm], + ["KGamePropertyBase", "typeinfo", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + # + # TBD: support various templates. + # + ["KGamePropertyHandler", "dict", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KMessageClient", "sendForward", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KMessageServer", "sendMessage", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Delete non-const. + # + ["KGame", "(p|inactiveP)layerList", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + # + # Unsupported signal argument type. + # + ["KGame", "signal(ReplacePlayerIO|LoadError)", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KGameIO", "signalPrepareTurn", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KGameKeyIO", "signalKeyEvent", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KGameMouseIO", "signalMouseEvent", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KGameProcessIO", "signalIOAdded", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KGamePropertyHandler", "signalSendMessage", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KMessageClient", "(forward|serverMessage)Received", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KMessageServer", "messageReceived", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Suppress template stuff for now. + # + ["KGameProperty", "operator.*|typeinfo|value", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + # + # Override the default "parent" rule. + # + ["KStandardGameAction", ".*", "parent", ".*", ".*", rule_helpers.noop] + ] + + +def modulecode(): + return { + "KF5KDEGames/highscore/highscoremod.sip": { + "code": + """ + class KgDifficulty /External/; + """ + }, + "KF5KDEGames/libkdegamesprivate/libkdegamesprivatemod.sip": { + "code": module_fix_mapped_types_private, + }, + "KF5KDEGames/libkdegamesprivate/kgame/kgamemod.sip": { + "code": module_fix_mapped_types, + }, + "KF5KDEGames/KDE/KDEmod.sip": { + "code": module_remove_redundant + } + } Index: find-modules/module_generation/PyKF5/KFace.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KFace.py @@ -0,0 +1,36 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KFace. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "QMatrix") + + +def modulecode(): + return { + "KFace/KFace/KFacemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KGAPI.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KGAPI.py @@ -0,0 +1,178 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KGAPI. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def fix_ktimezone_stuff(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "QNetworkAccessManager", "QNetworkRequest", + "QNetworkReply", "KDateTime", "KDateTime::Spec", "QLatin1String", "VObject") + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QList >") + fix_ktimezone_stuff(filename, sip, rule) + rule_helpers.module_add_classes(filename, sip, rule, "KConfigGroup") + rule_helpers.module_add_imports(filename, sip, rule, "KGAPI/KGAPI/Blogger/Bloggermod.sip", + "KGAPI/KGAPI/Calendar/Calendarmod.sip", + "KGAPI/KGAPI/Contacts/Contactsmod.sip", + "KGAPI/KGAPI/Drive/Drivemod.sip", + "KGAPI/KGAPI/Latitude/Latitudemod.sip", + "KGAPI/KGAPI/Tasks/Tasksmod.sip") + + +def module_fix_mapped_types_blogger(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", "QList", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QList >", + "QSharedPointer", "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, rule, "KConfig", "_IO_FILE", "Kleo::DownloadJob", + "Kleo::RefreshKeysJob", "QNetworkAccessManager", "QNetworkRequest", "QNetworkReply", + "Akonadi::Protocol::Command", "KConfigGroup", "Akonadi::ServerManagerPrivate", + "KCoreConfigSkeleton") + rule_helpers.module_add_includes(filename, sip, rule, "") + + +def module_fix_mapped_types_calendar(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", + "QList >", "QList >", + "QSharedPointer", "QSharedPointer", + "QSharedPointer", "QList >", + "QSharedPointer") + fix_ktimezone_stuff(filename, sip, rule) + + +def module_fix_mapped_types_contacts(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", + "QSharedPointer", "QList >", + "QList >", + "QSharedPointer", "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Job") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/KIOCoremod.sip") + + +def module_fix_mapped_types_drive(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", + "QList >", + "QList >", + "QList >", + "QList >", + "QList >", "QSharedPointer", + "QSharedPointer", + "QSharedPointer", + "QSharedPointer", + "QSharedPointer", + "QSharedPointer", + "QSharedPointer", + "QList >", + "QList >", + "QList >", + "QSharedPointer", + "QSharedPointer", + "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Job") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/KIOCoremod.sip") + + +def module_fix_mapped_types_latitude(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", + "QSharedPointer", "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Job") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/KIOCoremod.sip") + + +def module_fix_mapped_types_maps(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QVector") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Job") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/KIOCoremod.sip") + + +def module_fix_mapped_types_tasks(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", + "QList >", "QList >", + "QSharedPointer", "QSharedPointer", + "QSharedPointer") + fix_ktimezone_stuff(filename, sip, rule) + + +def container_rules(): + return [ + # + # SIP does not seem to be able to handle these type specialization, but we can live without them? + # + ["KGAPI2", "Account|Object|Reminder", "", ".*", ".*", rule_helpers.container_make_unassignable], + ] + + +def modulecode(): + return { + "KGAPI/KGAPI/KGAPImod.sip": { + "code": module_fix_mapped_types, + }, + "KGAPI/KGAPI/Blogger/Bloggermod.sip": { + "code": module_fix_mapped_types_blogger, + }, + "KGAPI/KGAPI/Calendar/Calendarmod.sip": { + "code": module_fix_mapped_types_calendar, + }, + "KGAPI/KGAPI/Contacts/Contactsmod.sip": { + "code": module_fix_mapped_types_contacts, + }, + "KGAPI/KGAPI/Drive/Drivemod.sip": { + "code": module_fix_mapped_types_drive, + }, + "KGAPI/KGAPI/Latitude/Latitudemod.sip": { + "code": module_fix_mapped_types_latitude, + }, + "KGAPI/KGAPI/Maps/Mapsmod.sip": { + "code": module_fix_mapped_types_maps, + }, + "KGAPI/KGAPI/Tasks/Tasksmod.sip": { + "code": module_fix_mapped_types_tasks, + }, + } Index: find-modules/module_generation/PyKF5/KGeoMap.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KGeoMap.py @@ -0,0 +1,53 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KGeoMap. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList") + rule_helpers.module_add_includes(filename, sip, rule, "") + rule_helpers.module_add_classes(filename, sip, rule, "Marble::GeoDataCoordinates", "KConfigGroup") + + +def container_rules(): + return [ + ["KGeoMap::AbstractMarkerTiler", "NonEmptyIterator", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + ["KGeoMap::TileIndex", "latLonIndex", ".*", "QPoint", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KGeoMap/KGeoMap/KGeoMapmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KGlobalAccel.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KGlobalAccel.py @@ -0,0 +1,51 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KGlobalAccel. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "OrgKdeKglobalaccelComponentInterface", "QAction", "QWidget") + + + +def module_fix_mapped_types_private(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList", + "QList") + rule_helpers.module_add_classes(filename, sip, entry, "OrgKdeKglobalaccelComponentInterface", "QAction", + "QWidget", "GlobalShortcutsRegistry", "QDBusContext") + + +def modulecode(): + return { + "KGlobalAccel/private/privatemod.sip": { + "code": module_fix_mapped_types_private, + }, + "KGlobalAccel/KGlobalAccelmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KHtml.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KHtml.py @@ -0,0 +1,97 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KHtml. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + rule_helpers.module_add_classes(filename, sip, entry, "DOM::CSSRuleImpl", "KIO::Job", "KXmlGuiWindow") + + +def module_fix_mapped_types_dom(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "DOM::AbstractViewImpl", "DOM::AttrImpl", + "DOM::CDATASectionImpl", "DOM::CharacterDataImpl", "DOM::CommentImpl", + "DOM::CounterImpl", "DOM::CSSCharsetRuleImpl", "DOM::CSSFontFaceRuleImpl", + "DOM::CSSImportRuleImpl", "DOM::CSSMediaRuleImpl", "DOM::CSSNamespaceRuleImpl", + "DOM::CSSPageRuleImpl", "DOM::CSSPrimitiveValueImpl", "DOM::CSSRuleImpl", + "DOM::CSSRuleListImpl", "DOM::CSSStyleDeclarationImpl", "DOM::CSSStyleRuleImpl", + "DOM::CSSStyleSheetImpl", "DOM::CSSUnknownRuleImpl", "DOM::CSSValueImpl", + "DOM::CSSValueListImpl", "DOM::DocumentFragmentImpl", "DOM::DocumentImpl", + "DOM::DocumentTypeImpl", "DOM::DOMImpl", "DOM::DOMImplementationImpl", + "DOM::DOMStringImpl", "DOM::ElementImpl", "DOM::EntityImpl", + "DOM::EntityReferenceImpl", "DOM::EventImpl", "DOM::HTMLAnchorElementImpl", + "DOM::HTMLAppletElementImpl", "DOM::HTMLAreaElementImpl", "DOM::HTMLBaseElementImpl", + "DOM::HTMLBaseFontElementImpl", "DOM::HTMLBodyElementImpl", "DOM::HTMLBRElementImpl", + "DOM::HTMLButtonElementImpl", "DOM::HTMLCollectionImpl", + "DOM::HTMLDirectoryElementImpl", "DOM::HTMLDivElementImpl", + "DOM::HTMLDListElementImpl", "DOM::HTMLDocumentImpl", "DOM::HTMLElementImpl", + "DOM::HTMLFieldSetElementImpl", "DOM::HTMLFontElementImpl", + "DOM::HTMLFormElementImpl", "DOM::HTMLFrameElementImpl", + "DOM::HTMLFrameSetElementImpl", "DOM::HTMLGenericElementImpl", + "DOM::HTMLGenericFormElementImpl", "DOM::HTMLHeadElementImpl", + "DOM::HTMLHRElementImpl", "DOM::HTMLHtmlElementImpl", "DOM::HTMLIFrameElementImpl", + "DOM::HTMLImageElementImpl", "DOM::HTMLInputElementImpl", + "DOM::HTMLIsIndexElementImpl", "DOM::HTMLLabelElementImpl", + "DOM::HTMLLayerElementImpl", "DOM::HTMLLegendElementImpl", "DOM::HTMLLIElementImpl", + "DOM::HTMLLinkElementImpl", "DOM::HTMLMapElementImpl", "DOM::HTMLMenuElementImpl", + "DOM::HTMLMetaElementImpl", "DOM::HTMLObjectBaseElementImpl", + "DOM::HTMLObjectElementImpl", "DOM::HTMLOListElementImpl", + "DOM::HTMLOptGroupElementImpl", "DOM::HTMLOptionElementImpl", + "DOM::HTMLParamElementImpl", "DOM::HTMLPartContainerElementImpl", + "DOM::HTMLPreElementImpl", "DOM::HTMLScriptElementImpl", "DOM::HTMLSelectElementImpl", + "DOM::HTMLStyleElementImpl", "DOM::HTMLTableCaptionElementImpl", + "DOM::HTMLTableCellElementImpl", "DOM::HTMLTableColElementImpl", + "DOM::HTMLTableElementImpl", "DOM::HTMLTableRowElementImpl", + "DOM::HTMLTableSectionElementImpl", "DOM::HTMLTextAreaElementImpl", + "DOM::HTMLTitleElementImpl", "DOM::HTMLUListElementImpl", "DOM::MediaListImpl", + "DOM::MouseEventImpl", "DOM::MutationEventImpl", "DOM::NamedNodeMapImpl", + "DOM::NodeFilterImpl", "DOM::NodeImpl", "DOM::NodeIteratorImpl", "DOM::NodeListImpl", + "DOM::NotationImpl", "DOM::ProcessingInstructionImpl", "DOM::RangeImpl", + "DOM::RectImpl", "DOM::StyleListImpl", "DOM::StyleSheetImpl", + "DOM::StyleSheetListImpl", "DOM::TextImpl", "DOM::TreeWalkerImpl", "DOM::UIEventImpl", + "KHTMLView") + + +def function_rules(): + return [ + # + # SIP overloaded functions with the same Python signature. + # + ["DOM::DOMString", "toInt", ".*", ".*", "", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KHtml/KHtmlmod.sip": { + "code": module_fix_mapped_types, + }, + "KHtml/dom/dommod.sip": { + "code": module_fix_mapped_types_dom, + }, + } Index: find-modules/module_generation/PyKF5/KI18n.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KI18n.py @@ -0,0 +1,48 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KI18n. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def container_rules(): + return [ + # + # Remove some useless stuff. + # + ["klocalizedstring.h", "I18nTypeCheck", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + ["Kuit", "setupForDomain", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Needs %MethodCode? + # + ["KuitSetup", "setTagPattern", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP thinks there are duplicate signatures. + # + ["KLocalizedString", "subs", ".*", ".*", "(?!long long).*", rule_helpers.function_discard], + ] Index: find-modules/module_generation/PyKF5/KIMAP.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIMAP.py @@ -0,0 +1,60 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIMAP. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def typedef_duplicate_discard(container, typedef, sip, rule): + if container.translation_unit.spelling.endswith("storejob.h"): + rule_helpers.typedef_discard(container, typedef, sip, rule) + else: + return rule_helpers.SILENT_NOOP + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QSharedPointer", + "QVector", "QMap") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", + "KIO::Connection", "KService", "KIO::ClipboardUpdater", "KIMAP::Message") + + +def container_rules(): + return [ + ["KIMAP", "Term", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + + +def typedef_rules(): + return [ + ["KIMAP", "MessageFlags", ".*", ".*", typedef_duplicate_discard], + ] + + +def modulecode(): + return { + "KIMAP/KIMAP/KIMAPmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KIO.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIO.py @@ -0,0 +1,588 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIOCore, PyKF5.KIOFileWidgets, +PyKF5.KIOGui PyKF5.KIOWidgets PyKF5.kio. This modules describes: + + * Supplementary SIP file generator rules. +""" +from clang.cindex import TokenKind, TypeKind + +import rule_helpers + + +def _container_discard_templated_bases_and_fake(container, sip, matcher): + rule_helpers.container_discard_templated_bases(container, sip, matcher) + rule_helpers.container_fake_derived_class(container, sip, matcher) + + +def _function_rewrite_using_decl1(container, function, sip, matcher): + sip["parameters"] = ["const QByteArray &data"] + sip["fn_result"] = "virtual void" + + +def _function_rewrite_using_decl2(container, function, sip, matcher): + sip["parameters"] = ["KIO::filesize_t size"] + sip["fn_result"] = "virtual void" + + +def variable_fully_qualify(container, variable, sip, matcher): + sip["decl"] = "KNTLM::" + sip["decl"] + + +def module_fix_KIOCore_kio(filename, sip, rule): + rule_helpers.module_delete_imports(filename, sip, rule, "KIOCore/KIO/KIOmod.sip") + rule_helpers.modulecode_delete(filename, sip, rule, "QMap") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::JobUiDelegateExtension /External/", + "KIO::MetaData /External/") + + +def module_fix_KIOCore_KIO(filename, sip, rule): + rule_helpers.module_delete_imports(filename, sip, rule, "KIOCore/KIOCoremod.sip") + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QMap", + "QVector") + rule_helpers.modulecode_make_local(filename, sip, rule, "QMap") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KIO::ClipboardUpdater", + "KConfigGroup /External/", "KFileItemList /External/", "KService /External/", + "KRemoteEncoding /External/", "QDBusArgument /External/") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + + +def module_fix_KIOGui_KIO(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip", "KIOCore/KIO/KIOmod.sip", + "KIOCore/KIOCoremod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KService", "KIO::Connection", "KIO::ClipboardUpdater") + + +def module_fix_KIOWidgets_KIO(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QList") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip", "KIOCore/KIO/KIOmod.sip", + "KIOCore/KIOCoremod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KService", "KPixmapSequence") + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, rule, "QList >") + rule_helpers.modulecode_delete(filename, sip, rule, "QList") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KService", "KSslCertificateBoxPrivate", "KIO::Connection", + "KIO::ClipboardUpdater") + + +def module_fix_mapped_types_filewidgets(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList", "QVector") + + +def module_fix_mapped_types_widgets(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QList", + "QList", "QExplicitlySharedDataPointer", + "QList >") + rule_helpers.modulecode_make_local(filename, sip, rule, "QList") + rule_helpers.module_add_classes(filename, sip, rule, "KCModule", "KSslCertificateBoxPrivate") + + +def module_fix_includes_kimagefilepreview(filename, sip, rule): + rule_helpers.module_add_includes(filename, sip, rule, "", "") + + +def module_fix_includes_metainfojob(filename, sip, rule): + rule_helpers.module_add_includes(filename, sip, rule, "", "", "") + + +def container_rules(): + return [ + ["kfileitem.h", "KFileItemList", ".*", ".*", ".*", _container_discard_templated_bases_and_fake], + ["kmountpoint.h", "KMountPoint", ".*", ".*", ".*QSharedData.*", rule_helpers.container_discard_QSharedData_base], + ["KIO", "MetaData", ".*", ".*", ".*", _container_discard_templated_bases_and_fake], + ["KIO", "DesktopExecParser", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + # + # Remove some inlined stuff. + # + ["udsentry.h", "debugUDSEntry", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Privates... + # + ["KIO::EmptyTrashJob", "EmptyTrashJob", ".*", ".*", ".*Private.*", rule_helpers.function_discard], + ["KIO::FileSystemFreeSpaceJob", "FileSystemFreeSpaceJob", ".*", ".*", ".*Private.*", rule_helpers.function_discard], + ["KIO::MkdirJob", "MkdirJob", ".*", ".*", ".*Private.*", rule_helpers.function_discard], + # + # Duplicate signatures. + # + ["KIO::StatJob", "setSide", ".*", ".*", ".*bool.*", rule_helpers.function_discard], + ["KFileItem", "mostLocalUrl", ".*", ".*", ".*local", rule_helpers.function_discard], + # + # Missing stat64. + # + ["KIO::UDSEntry", "UDSEntry", ".*", ".*", ".*stat64.*", rule_helpers.function_discard], + # + # Rewrite using declaration. + # + ["KIO::TCPSlaveBase", "write", ".*", ".*", "", _function_rewrite_using_decl1], + ["KIO::TCPSlaveBase", "read", ".*", ".*", "", _function_rewrite_using_decl2], + # + # Deleted functions. + # + ["KIO", "file_(copy|move)", ".*", ".*", ".*flags", rule_helpers.function_discard], + # + # SIP needs help with the semantics of QList >. + # + ["KACL", "setAll(User|Group)Permissions", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def typedef_rules(): + return [ + # + # Remove some useless stuff. + # + ["kacl.h", "ACL.*PermissionsIterator|ACL.*PermissionsConstIterator", ".*", ".*", rule_helpers.typedef_discard], + ["kprotocolmanager.h", "KSharedConfigPtr", ".*", ".*", rule_helpers.typedef_discard], + ["thumb.*creator.h", "newCreator", ".*", ".*", rule_helpers.typedef_discard], + ] + + +def variable_rules(): + return [ + # + # Fully-qualify. + # + ["KNTLM::.*", ".*", "SecBuf.*", variable_fully_qualify], + ] + + +def modulecode(): + return { + "KIOCore/KIOCoremod.sip": { + "code": module_fix_mapped_types, + }, + "KIOCore/kio/kiomod.sip": { + "code": module_fix_KIOCore_kio, + }, + "KIOCore/KIO/KIOmod.sip": { + "code": module_fix_KIOCore_KIO, + }, + "KIOGui/KIO/KIOmod.sip": { + "code": module_fix_KIOGui_KIO, + }, + "KIOWidgets/KIO/KIOmod.sip": { + "code": module_fix_KIOWidgets_KIO, + }, + "KIOWidgetsmod.sip": { + "code": module_fix_mapped_types_widgets, + }, + "KIOFileWidgets/KIOFileWidgetsmod.sip": { + "code": module_fix_mapped_types_filewidgets, + }, + "KIOFileWidgets/KImageFilePreview": { + "code": module_fix_includes_kimagefilepreview + }, + "MetaInfoJob": { + "code": module_fix_includes_metainfojob + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kprotocolinfo.h::KProtocolInfo": { # KProtocolInfo : KSycocaEntry + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KSycocaEntry' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KProtocolInfo; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KService; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KServiceGroup; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KServiceType; + if (dynamic_cast(sipCpp)) + sipType = sipType_KMimeType; + } + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KIO::TCPSlaveBase": { # TCPSlaveBase : KIO::SlaveBase + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'SlaveBase' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_ForwardingSlaveBase; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_TCPSlaveBase; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED karchivedirectory.h::KArchiveDirectory": { # KArchiveDirectory : KArchiveEntry + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KArchiveEntry' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KArchiveDirectory; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KArchiveFile; + if (dynamic_cast(sipCpp)) + sipType = sipType_KZipFileEntry; + } + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KIO::AccessManager": { # AccessManager : QNetworkAccessManager + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'AccessManager' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_AccessManager; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KIO::Integration::CookieJar": { # CookieJar : QNetworkCookieJar + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'CookieJar' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_Integration_CookieJar; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kar.h::KAr": { # KAr : KArchive + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KArchive' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KAr; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KZip; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED thumbcreator.h::ThumbCreator": { # ThumbCreator + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'ThumbCreator' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_ThumbCreatorV2; + else if (dynamic_cast(sipCpp)) + sipType = sipType_ThumbSequenceCreator; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kurlpixmapprovider.h::KUrlPixmapProvider": { # KUrlPixmapProvider : KPixmapProvider + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'KPixmapProvider' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KUrlPixmapProvider; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KAbstractFileModule": { # KAbstractFileModule : QObject + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KAbstractFileItemActionPlugin; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KAbstractFileModule; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KAutoMount; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KAutoUnmount; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBookmarkDomBuilder; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KBookmarkImporterBase; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCrashBookmarkImporterImpl; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIEBookmarkImporterImpl; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KNSBookmarkImporterImpl; + if (dynamic_cast(sipCpp)) + sipType = sipType_KMozillaBookmarkImporterImpl; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KOperaBookmarkImporterImpl; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KXBELBookmarkImporterImpl; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBookmarkManager; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KBookmarkMenu; + if (dynamic_cast(sipCpp)) + sipType = sipType_KonqBookmarkMenu; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KUrlCompletion; + if (dynamic_cast(sipCpp)) + sipType = sipType_KShellCompletion; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCrashBookmarkImporter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDataTool; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDirLister; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDirWatch; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDiskFreeSpace; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileItemActionPlugin; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileItemActions; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFilePreviewGenerator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileWritePlugin; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_Connection; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_ConnectionServer; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_FileUndoManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_ForwardingSlaveBase; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_NetAccess; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_Scheduler; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_SessionData; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_SlaveConfig; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KIO_SlaveInterface; + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_Slave; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KIO_Job; + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_ChmodJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_CopyJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_DeleteJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_DirectorySizeJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_FileCopyJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_MetaInfoJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_PreviewJob; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KIO_SimpleJob; + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_FileJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_ListJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_StatJob; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KIO_TransferJob; + if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_DavJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_MimetypeJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_MultiGetJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_SpecialJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_StoredTransferJob; + } + } + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_JobUiDelegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNFSShare; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KPropertiesDialogPlugin; + if (dynamic_cast(sipCpp)) + sipType = sipType_KFileSharePropsPlugin; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KRun; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSambaShare; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUriFilterPlugin; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileItemDelegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDeviceListModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDirModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFilePlacesModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDirSortFilterProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBookmarkActionMenu; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNewFileMenu; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBookmarkAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDataToolAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDirOperator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileMetaDataConfigurationWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileMetaDataWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_RenameDialogPlugin; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KPreviewWidgetBase; + if (dynamic_cast(sipCpp)) + sipType = sipType_KImageFilePreview; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KStatusBarOfflineIndicator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUrlNavigator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIconButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFileFilterCombo; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUrlComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBookmarkDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDirSelectDialog; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KFileDialog; + if (dynamic_cast(sipCpp)) + sipType = sipType_KEncodingFileDialog; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_SkipDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIconDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMimeTypeChooserDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNameAndUrlInputDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KOpenWithDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KOCRDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPropertiesDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KScanDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_PasswordDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUrlRequesterDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIO_RenameDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBuildSycocaProgressDialog; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KUrlRequester; + if (dynamic_cast(sipCpp)) + sipType = sipType_KUrlComboRequester; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMimeTypeChooser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFilePlacesView; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIconCanvas; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBookmarkContextMenu; + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/KIOFileWidgets.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIOFileWidgets.py @@ -0,0 +1,42 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIOFileWidgets. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QList", "QVector") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KIO::ClipboardUpdater", "KCModule", + "KActionCollection", "KToolBar", "KIO::PreviewJob") + + +def modulecode(): + return { + "KIOFileWidgets/KIOFileWidgetsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KIOWidgets.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIOWidgets.py @@ -0,0 +1,53 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIOWidgets. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QExplicitlySharedDataPointer", + "QList", "QList", "QList", + "QList >", "QList") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KIO::ClipboardUpdater", + "KSslCertificateBoxPrivate", "KCModule") + + +def module_fix_mapped_types_kio(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KService", "KIO::ClipboardUpdater", + "KPixmapSequence") + + +def modulecode(): + return { + "KIOWidgets/KIOWidgetsmod.sip": { + "code": module_fix_mapped_types, + }, + "KIOWidgets/KIO/KIOmod.sip": { + "code": module_fix_mapped_types_kio, + }, + } Index: find-modules/module_generation/PyKF5/KIPI.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIPI.py @@ -0,0 +1,49 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIPI. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QExplicitlySharedDataPointer", + "QMap", "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KXmlGuiWindow") + + +def container_rules(): + return [ + ["KIPI", "EditHintScope", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["KIPI::PluginLoader", "Info", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def modulecode(): + return { + "KIPI/KIPI/KIPImod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KIconThemes.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIconThemes.py @@ -0,0 +1,50 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIconThemes. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QList") + rule_helpers.module_add_imports(filename, sip, rule, "KWidgetsAddons/KWidgetsAddonsmod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KConfig") + + +def function_rules(): + return [ + # + # This class has inline implementations in the header file. + # + ["kiconloader.h", "operator\+\+", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KIconThemes/KIconThemesmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KIdentityManagement.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIdentityManagement.py @@ -0,0 +1,50 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIdentityManagement. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def container_fix_types(container, sip, rule): + clazz = "QList" + rule_helpers.container_add_typedefs(container, sip, rule, clazz + "::Iterator", + clazz + "::ConstIterator", clazz + "::iterator", + clazz + "::const_iterator") + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QSet") + rule_helpers.module_add_classes(filename, sip, rule, "KPIMTextEdit::RichTextComposer") + + +def container_rules(): + return [ + ["KIdentityManagement", "IdentityManager", ".*", ".*", ".*", container_fix_types], + ] + + +def modulecode(): + return { + "KIdentityManagement/KIdentityManagement/KIdentityManagementmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KIdleTime.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KIdleTime.py @@ -0,0 +1,40 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KIdleTime. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types_private(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + + +def modulecode(): + return { + "KIdleTime/private/privatemod.sip": { + "code": module_fix_mapped_types_private, + }, + } Index: find-modules/module_generation/PyKF5/KItemModels.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KItemModels.py @@ -0,0 +1,39 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KItemModels. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QVector") + + +def modulecode(): + return { + "KItemModels/KItemModelsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KItemViews.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KItemViews.py @@ -0,0 +1,67 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KItemViews. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers +from templates import PyQt +import templates.methodcode + + +class FunctionExpander(templates.methodcode.FunctionExpander): + """ + Override the use of the protected enum 'QAbstractItemView::CursorAction'. + """ + def analyse_function(self, fn, cursor, sip): + entries = super(FunctionExpander, self).analyse_function(fn, cursor, sip) + assert entries["parameters"][0].cxx_t == "QAbstractItemView::CursorAction" + entries["parameters"][0] = PyQt.FunctionParameterHelper("int", None) + entries["p_types"][0] = "int" + return entries + + +def function_uses_templates(container, function, sip, matcher): + sip.setdefault("template", FunctionExpander) + PyQt.function_uses_templates(container, function, sip, matcher) + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QVector", "QList") + + +def function_rules(): + return [ + # + # Rewrite using declaration. + # + ["KCategorizedView", "moveCursor", ".*", ".*", ".*", function_uses_templates], + ] + + +def modulecode(): + return { + "KItemViews/KItemViewsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KJS.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KJS.py @@ -0,0 +1,406 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KJS. This modules describes: + + * Supplementary SIP file generator rules. +""" +from clang.cindex import StorageClass + +import builtin_rules +import rule_helpers +import rules_engine + + +def function_discard_inlines(container, fn, sip, rule): + if fn.storage_class == StorageClass.NONE: + rule_helpers.function_discard(container, fn, sip, rule) + + +def container_fix_parser(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::RefPtr", + "WTF::PassRefPtr") + + +def container_fix_idrephash(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::RefPtr") + # + # No subclassing from %MappedType. See... + # + # https://www.riverbankcomputing.com/pipermail/pyqt/2017-August/039513.html + # + sip["base_specifiers"] = [] + + +def container_fix_rep(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::PassRefPtr") + + +def container_fix_template_w_literal_compilestate(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::Vector", + "WTF::Vector") + + +def container_fix_template_w_literal_localstorage(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::Vector") + + +def container_fix_template_w_literal_opcodes(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::Vector") + + +def container_fix_template_w_literal_nodes(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::Vector") + + +def container_fix_template_w_literal_ustring(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "WTF::Vector") + + +def container_fix_fn_ptr(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "bool (*f)(int)", "WTF::Vector") + + +def container_fix_typename_countedset(container, sip, rule): + clazz = "typename HashMap >" + rule_helpers.container_add_typedefs(container, sip, rule, clazz + "::iterator", + clazz + "::const_iterator") + + +def container_fix_typename_hashmaphm(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename KeyTraitsArg::TraitType", + "typename MappedTraitsArg::TraitType", + "typename PairHashTraits::TraitType", + "HashMap", + "HashTableIteratorAdapter, HashArg, PairHashTraits, KeyTraitsArg>, __HashMap2_t>") + + +def container_fix_typename_hashmappfe(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename PairType::first_type") + + +def container_fix_typename_hashmaphmt(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename ValueType::first_type", + "typename ValueType::second_type") + + +def container_fix_typename_hashset(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename KeyTraitsArg::TraitType", + "HashSet", "typename TraitsArg::TraitType", + "HashTableIteratorAdapter, HashArg, TraitsArg, TraitsArg>, " + "__HashSet2_t>") + + +def container_fix_typename_hashtable(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, + "HashTableIterator", + "HashTableConstIterator", + "IdentityHashTranslator") + + +def container_fix_typename_hashtableia(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename HashTableType::const_iterator", + "typename HashTableType::iterator") + + +def container_fix_typename_hashtraits(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename FirstTraitsArg::TraitType", + "typename SecondTraitsArg::TraitType", + "GenericHashTraits >") + # + # No subclassing from %MappedType. See... + # + # https://www.riverbankcomputing.com/pipermail/pyqt/2017-August/039513.html + # + sip["base_specifiers"] = [] + + +def container_fix_typename_refptrhashmap(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "typename ValueType::first_type", + "typename ValueType::second_type", "typename ValueTraits::FirstTraits", + "typename ValueTraits::SecondTraits") + + +def function_deref(container, fn, sip, rule): + rule_helpers.initialise_cxx_decl(sip) + # + # Strip the "&". + # + sip["fn_result"] = sip["fn_result"][:-2] + code = """%MethodCode +// TBD +%End +""" + sip["code"] += code + + +def function_n_callable(container, fn, sip, rule): + rule_helpers.initialise_cxx_decl(sip) + t, v = sip["parameters"][-1].split(None, 1) + sip["parameters"][-1] = "SIP_PYCALLABLE " + v + + +def function_2plus_callable(container, fn, sip, rule): + rule_helpers.initialise_cxx_decl(sip) + for i in range(2, len(sip["parameters"])): + t, v = sip["parameters"][i].rsplit(None, 1) + sip["parameters"][i] = "SIP_PYCALLABLE " + v + + +def variable_rewrite_array(container, variable, sip, rule): + builtin_rules.variable_rewrite_extern(container, variable, sip, rule) + sip["code"] += """ +{ +%GetCode +// TODO +%End +}""" + + +def variable_rewrite_array_rw(container, variable, sip, rule): + builtin_rules.variable_rewrite_extern(container, variable, sip, rule) + variable_rewrite_rw(container, variable, sip, rule) + + +def variable_rewrite_rw(container, variable, sip, rule): + sip["code"] += """ +{ +%GetCode +// TODO +%End + +%SetCode +// TODO +%End +}""" + + +def module_discard_duplicate(filename, sip, rule): + sip["name"] = "" + + +def module_fix_bytecode(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "DOM::DOMString") + +def module_fix_kjs(filename, sip, rule): + # + # Fixup the recursion. + # + lines = [] + for l in sip["decl"].split("\n"): + if "kjs/bytecode/bytecodemod.sip" in l: + # + # These modules refer to each other. + # + lines.append("%If (KJsEmbed_KJsEmbed_KJsEmbedmod)") + lines.append(l) + lines.append("%End") + continue + lines.append(l) + sip["decl"] = "\n".join(lines) + rule_helpers.module_add_classes(filename, sip, rule, "DOM::DOMString") + + +def module_fix_wtf(filename, sip, rule): + rule_helpers.module_delete_imports(filename, sip, rule, "kjs/kjsmod.sip") + + +def container_rules(): + return [ + # + # SIP cannot handle templates with literal parameters. + # https://www.riverbankcomputing.com/pipermail/pyqt/2017-July/039390.html + # + ["KJS", "CompileState", ".*", ".*", ".*", container_fix_template_w_literal_compilestate], + ["KJS", "CodeGen", ".*", ".*", ".*", container_fix_template_w_literal_opcodes], + ["KJS", "FunctionBodyNode", ".*", ".*", ".*", container_fix_template_w_literal_nodes], + ["KJS", "Parser", ".*", ".*", ".*", container_fix_parser], + ["KJS", "IdentifierRepHash", ".*", ".*", ".*", container_fix_idrephash], + ["KJS", "UString", ".*", ".*", ".*", container_fix_template_w_literal_ustring], + ["KJS::UString", "Rep", ".*", ".*", ".*", container_fix_rep], + # + # SIP cannot handle function pointer arguments. + # + ["KJS", "Lexer", ".*", ".*", ".*", container_fix_fn_ptr], + # + # SIP cannot handle "typename ...". + # + ["WTF", "HashCountedSet", ".*", ".*", ".*", container_fix_typename_countedset], + ["WTF", "HashMap", ".*", ".*", ".*", container_fix_typename_hashmaphm], + ["WTF", "PairFirstExtractor", ".*", ".*", ".*", container_fix_typename_hashmappfe], + ["WTF", "HashMapTranslator", ".*", ".*", ".*", container_fix_typename_hashmaphmt], + ["WTF", "HashTable", ".*", ".*", ".*", container_fix_typename_hashtable], + ["WTF", "HashTable(Const|)IteratorAdapter", ".*", ".*", ".*", container_fix_typename_hashtableia], + ["WTF", "HashSet", ".*", ".*", ".*", container_fix_typename_hashset], + ["WTF", "PairHashTraits", ".*", ".*", ".*", container_fix_typename_hashtraits], + ["WTF", "RefPtrHashMapRawKeyTranslator", ".*", ".*", ".*", container_fix_typename_refptrhashmap], + # + # SIP does not seem to be able to handle these type specialization, but we can live without them? + # + ["KJS", "CellSize", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "DefaultHash", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "GenericHashTraitsBase", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "HashTraits", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "IntTypes", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "IsInteger", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "IsPod", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "Mover", ".*", ".*", ".*", rule_helpers.container_discard], + ["WTF", "PtrHash", "P", ".*", ".*", rule_helpers.container_discard], + ["WTF", "Vector(Destructor|Initializer|Mover|Copier|Filler|Comparer|Buffer|(Traits(Base|)))", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def forward_declaration_rules(): + return [ + ["KJS", "AttachedInterpreter", ".*", rule_helpers.noop], + ["date_object.h", "tm", ".*", rule_helpers.noop], + ["kjsarguments.h", "KJSArgumentsHandle", ".*", rule_helpers.noop], + ["kjscontext.h", "KJSContextHandle", ".*", rule_helpers.noop], + ["kjsinterpreter.h", "KJSInterpreterHandle", ".*", rule_helpers.noop], + ["kjsobject.h", "KJSObjectHandle", ".*", rule_helpers.noop], + ["KJS", "SourceStream", ".*", rule_helpers.noop], + ["ustring.h", "QConstString", ".*", rule_helpers.noop], + ] + + +def function_rules(): + return [ + ["KJS", "jsString", ".*", ".*", "const char \*__0.*", rule_helpers.function_discard], + ["KJS", "jsNumber", ".*", ".*", ".*(int|long).*", rule_helpers.function_discard], + ["KJS::Collector", "cellBlock", ".*", "(?!const ).*", ".*", rule_helpers.function_discard], + ["KJS::Collector", "rootObjectTypeCounts|markOtherThreadConservatively", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::FunctionBodyNode", "getFunctionLocalInfo", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::JSImmediate", "from", ".*", ".*", "(?!long long ).*", rule_helpers.function_discard], + ["KJS::JSImmediate", "getNumber", ".*", "double", ".*", rule_helpers.function_discard], + ["KJS::JSImmediate", "getTruncatedInt32", ".*", "int", ".*", rule_helpers.function_discard], + ["KJS::JSImmediate", "getTruncatedUInt32", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::JSValue", "get(Boolean|Number)", ".*", ".*", "", rule_helpers.function_discard], + ["KJS::JSValue", "getObject|asCell", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KJS::JSValue", "to(U|)Int32", ".*", ".*", "KJS::ExecState \*__0", rule_helpers.function_discard], + ["KJS::JSCell", "getNumber", ".*", ".*", "", rule_helpers.function_discard], + ["KJS::JSCell", "getObject", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KJS::JSObject", "getDirect(Write|)Location", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::Package", "parent", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KJS::PropertyMap", "get", ".*", ".*", ".*name", rule_helpers.function_discard], + ["KJS::PropertyMap", "get(Write|)Location", ".*", ".*", ".*name", rule_helpers.function_discard], + ["KJS::PropertyNameArray", "operator\[\]", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KJSInterpreter", "globalContext", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KJSObject", "setProperty", ".*", ".*", ".*(bool |double |const char *)value", rule_helpers.function_discard], + ["KJS::RegExp", "match", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::RegExpObjectImp", "performMatch", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::UChar", "UChar", ".*", ".*", ".*char.*", rule_helpers.function_discard], + ["KJS::CString", "CString", ".*", ".*", "const char \*c", rule_helpers.function_discard], + ["KJS::UString", "from", ".*", ".*", ".*(int|double).*", rule_helpers.function_discard], + # + # Callables. + # + ["KJS::UnicodeSupport", "setIdent(Start|Part)Checker|setTo(Lower|Upper)Function", ".*", ".*", ".*", function_n_callable], + ["KJS::PropertySlot", "setCustomIndex", ".*", ".*", ".*GetValueNumberFunc getValue", rule_helpers.function_discard], + ["KJS::PropertySlot", "set(StaticEntry|Custom.*)", ".*", ".*", ".*", function_n_callable], + ["KJS::StringProtoFunc", "setTo(Lower|Upper)Function", ".*", ".*", ".*", function_n_callable], + ["KJSPrototype", "define(Property|Function)", ".*", ".*", ".*", function_2plus_callable], + # + # Inline implementations. + # + ["KJS::JS(Cell|Value)", "isObject", ".*", ".*", ".*", ".*inline .*", ".*", rule_helpers.function_discard], + ["KJS", "is(NaN|Finite|(Pos|Neg|)Inf)|signBit", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::OpValue", "OpValue", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::JSVariableObject", "~JSVariableObject", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::JSWrapperObject", "JSWrapperObject", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::JSObject", "JSObject", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::PropertyMap", "PropertyMap", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::UChar", "UChar", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::UString", "UString", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::JSValue", "(~|)JSValue", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["KJS::JSCell", "(~|)JSCell", ".*", ".*", ".*", ".*inline .*", "", rule_helpers.function_discard], + ["MathExtras.h", ".*", ".*", "float", ".*", rule_helpers.function_discard], + ["WTF", "(is|to)ASCII.*", ".*", ".*", "(char|unsigned short|int) c", rule_helpers.function_discard], + ["WTF", "intHash", ".*", ".*", "unsigned (char|short|int) key.*", rule_helpers.function_discard], + # + # Get rid of operators we don't like. + # + ["KJS::JSCell", "operator.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::ListIterator", "operator.*", ".*", "KJS::JSValue.*", ".*", rule_helpers.function_discard], + ["KJS::ProtectedPtr", "operator.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KJS::ScopeChainIterator", "operator.*", ".*", "KJS.*", ".*", rule_helpers.function_discard], + ["KJS::FunctionBodyNode", "code", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["WTF::HashTable(Const|)(Keys|Values|)Iterator(Adapter|)", "operator.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["WTF::Own(Array|)Ptr", "operator.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["WTF::(Pass|)RefPtr", "operator.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["WTF::SharedPtr", "operator.*", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Return POD via reference. + # + ["KJS::JSVariableObject", "lengthSlot", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["KJS::JSVariableObject", "lengthSlot|tearOffNeededSlot", ".*", ".*", ".*", function_deref], + ] + + +def typedef_rules(): + return [ + ["KJS", "CodeBlock*", ".*", ".*", rule_helpers.typedef_discard], + ["KJS", "LocalStorage*", ".*", ".*", rule_helpers.typedef_discard], + ["WTF", "dummyWTF_.*", ".*", ".*", rule_helpers.typedef_discard], + ["WTF::HashMap", "(Key|Mapped|Value)Type", ".*", ".*", rule_helpers.typedef_discard], + ["WTF::Own(Array|)Ptr", "UnspecifiedBoolType", ".*", ".*", rule_helpers.typedef_discard], + ["WTF::(Pass|)RefPtr", "UnspecifiedBoolType", ".*", ".*", rule_helpers.typedef_discard], + ] + + +def variable_rules(): + return [ + ["KJS", "OpTypeVals|ConvOpVals|OpNameVals|OpByteCodeVals|opsForOpCodes|opSpecializations|opTypeIsAlign8", ".*", variable_rewrite_array], + ["KJS::Error", "errorNames", ".*", variable_rewrite_array_rw], + ["KJS::OpValue", "value", ".*", variable_rewrite_rw], + ["KJS::CollectorCell::.*", "freeCell", ".*", variable_rewrite_rw], + ["KJS::CollectorCell", "u", ".*", variable_rewrite_rw], + ["KJS::LocalStorageEntry", "val", ".*", variable_rewrite_rw], + ["YYSTYPE", ".*", ".* \*", rule_helpers.variable_discard], + ] + + +def unexposed_rules(): + return [ + # + # Random crap we don't like. + # + ["WTF", "HashMap", "template.*HashMap", rule_helpers.unexposed_discard], + ] + + +def modulecode(): + return { + "kjs/context.h": { + "code": module_discard_duplicate, + }, + "kjs/bytecode/bytecodemod.sip": { + "code": module_fix_bytecode, + }, + "kjs/kjsmod.sip": { + "code": module_fix_kjs, + }, + "wtf/wtfmod.sip": { + "code": module_fix_wtf, + }, + } Index: find-modules/module_generation/PyKF5/KJobWidgets.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KJobWidgets.py @@ -0,0 +1,51 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KJobWidgets. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import common_typecode +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QPair") + rule_helpers.module_add_imports(filename, sip, rule, "QtWidgets/QtWidgetsmod.sip") + + +def modulecode(): + return { + "KJobWidgets/KJobWidgetsmod.sip": { + "code": module_fix_mapped_types, + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kabstractwidgetjobtracker.h::KAbstractWidgetJobTracker": { + "code": common_typecode._kdeui_qobject_ctscc + }, + } Index: find-modules/module_generation/PyKF5/KLDAP.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KLDAP.py @@ -0,0 +1,62 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KLDAP. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def variable_customise(container, variable, sip, matcher): + sip["code"] = """ +{ +%GetCode +#include +%End +%SetCode +#include +%End +}""" + + +def module_fix_mapped_types(filename, sip, entry): + rule_helpers.modulecode_make_local(filename, sip, entry, "QList", "QList") + + +def function_rules(): + return [ + ["KLDAP::LdapOperation", "bind|bind_s", ".*", ".*", ".*", rule_helpers.function_discard], + ["KLDAP::LdapUrl", "extension", ".*", "QString", ".*", rule_helpers.function_discard], + ] + + +def variable_rules(): + return [ + ["KLDAP::LdapOperation.*", "proc", "SASL_Callback_Proc.*", variable_customise], + ] + + +def modulecode(): + return { + "KLDAP/KLDAP/KLDAPmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KMbox.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KMbox.py @@ -0,0 +1,41 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KMbox. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, rule, "KConfigGroup", "KCoreConfigSkeleton", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def modulecode(): + return { + "KMbox/KMbox/KMboxmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KMime.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KMime.py @@ -0,0 +1,81 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KMime. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers +from templates import PyQt + + +def _delete_duplicate_content(filename, sip, entry): + sip["decl"] = "" + + +def parameter_in_out(container, function, parameter, sip, matcher): + rule_helpers.parameter_out(container, function, parameter, sip, matcher) + rule_helpers.parameter_in(container, function, parameter, sip, matcher) + + +def parameter_out(container, function, parameter, sip, matcher): + rule_helpers.parameter_out(container, function, parameter, sip, matcher) + if sip["decl"].startswith("QPair"): + PyQt.pair_parameter(container, function, parameter, sip, matcher) + elif sip["decl"].startswith("QVector"): + PyQt.list_parameter(container, function, parameter, sip, matcher) + elif sip["decl"].startswith("QMap"): + PyQt.dict_parameter(container, function, parameter, sip, matcher) + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QMap", "QVector") + rule_helpers.module_add_classes(filename, sip, rule, "KConfigGroup", "KCoreConfigSkeleton", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def container_rules(): + return [ + # + # Duplicate Akonadi::SuperClass. + # + ["kmime_newsarticle.h", "Akonadi", ".*", ".*", ".*", rule_helpers.container_discard], + ["Akonadi", "MessageFolderAttribute", ".*", ".*", ".*", rule_helpers.container_make_unassignable], + ] + + +def parameter_rules(): + return [ + ["KMime::HeaderParsing", "parse.*", "scursor", ".*", ".*", parameter_in_out], + ["KMime::HeaderParsing", "parse.*", "result", ".*", ".*", parameter_out], + ] + + +def modulecode(): + return { + "KMime/KMime/KMimeMessage": { + "code": _delete_duplicate_content + }, + "KMime/KMime/KMimemod.sip": { + "code": module_fix_mapped_types + }, + } Index: find-modules/module_generation/PyKF5/KNewStuff3.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KNewStuff3.py @@ -0,0 +1,49 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KNewStuff3. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QExplicitlySharedDataPointer") + rule_helpers.module_add_classes(filename, sip, entry, "KNSCore::Engine") + + +def module_fix_mapped_types_core(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "Attica::Provider", "KArchiveDirectory") + rule_helpers.module_add_imports(filename, sip, rule, "KNewStuff3/KNS3/KNS3mod.sip") + + +def modulecode(): + return { + "KNewStuff3/KNS3/KNS3mod.sip": { + "code": module_fix_mapped_types, + }, + "KNewStuff3/KNSCore/KNSCoremod.sip": { + "code": module_fix_mapped_types_core, + }, + } Index: find-modules/module_generation/PyKF5/KNotifications.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KNotifications.py @@ -0,0 +1,61 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KNotifications. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def _function_rewrite_using_decl(container, function, sip, matcher): + sip["parameters"] = ["QEvent *event"] + sip["fn_result"] = "bool" + sip["prefix"] = "virtual " + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QPair", "QList", + "QList") + + +def container_rules(): + return [ + ["knotifyconfig.h", "KNotifyConfig", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + # + # Rewrite using declaration. + # + ["KNotification", "event", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def modulecode(): + return { + "KNotifications/KNotificationsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KNotifyConfig.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KNotifyConfig.py @@ -0,0 +1,32 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KNotifyConfig. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def forward_declaration_rules(): + return [ + ["knotifyconfigwidget.h", "KNotifyConfigElement", ".*", rule_helpers.forward_declaration_mark_external], + ] + Index: find-modules/module_generation/PyKF5/KPIMTextEdit.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KPIMTextEdit.py @@ -0,0 +1,47 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KPIMTextEdit. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList") + rule_helpers.module_add_classes(filename, sip, rule, "KPIMTextEdit::PlainTextEditor", "KConfig", + "KActionCollection", "KPIMTextEdit::NestedListHelper") + + +def container_rules(): + return [ + ["KPIMTextEdit", "InsertHtmlEditor", ".*", ".*", ".*", rule_helpers.container_make_uncopyable], + ] + + +def modulecode(): + return { + "KPIMTextEdit/KPIMTextEdit/KPIMTextEditmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KPackage.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KPackage.py @@ -0,0 +1,49 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KPackage. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + + +def function_rules(): + return [ + # + # SIP cannot handle std::function. + # + ["KPackage::PackageLoader", "findPackages", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KPackage/KPackage/KPackagemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KParts.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KParts.py @@ -0,0 +1,73 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KParts. This modules describes: + + * Supplementary SIP file generator rules. +""" +from clang.cindex import CursorKind + +import rule_helpers + + +def _function_rewrite_using_decl(container, fn, sip, rule): + if fn.kind == CursorKind.USING_DECLARATION: + sip["parameters"] = ["QObject *parent /TransferThis/", + "KXMLGUIClient *parentGUIClient", + "const KAboutData &aboutData"] + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QExplicitlySharedDataPointer", "QList", + "QMap") + rule_helpers.module_add_imports(filename, sip, rule, "SonnetCore/Sonnet/Sonnetmod.sip", "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KIconLoader", "KXmlGuiWindow", "KSslCertificateBoxPrivate", + "KIO::Connection", "KIO::ClipboardUpdater") + + +def function_rules(): + return [ + # + # SIP unsupported signal argument type. + # + ["KParts::BrowserExtension", "createNewWindow", ".*", ".*", ".*", rule_helpers.function_discard], + ["KParts::ReadWritePart", "sigQueryClose", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP overloaded functions with the same Python signature. + # + ["KParts::OpenUrlArguments", "metaData", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + # + # Rewrite using declaration. + # + ["KParts::Part", "loadPlugins", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def modulecode(): + return { + # + # NOTE: there are two files with the same name! + # + "KParts/KParts/KPartsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KPlotting.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KPlotting.py @@ -0,0 +1,45 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KPlotting. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + + +def function_rules(): + return [ + ["KPlotWidget", "axis", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KPlotting/KPlottingmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KPty.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KPty.py @@ -0,0 +1,30 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KPty. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def forward_declaration_rules(): + return [ + ["kpty.h", "termios", ".*", rule_helpers.noop], + ] Index: find-modules/module_generation/PyKF5/KRunner.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KRunner.py @@ -0,0 +1,42 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KRunner. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QExplicitlySharedDataPointer", "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KSycoca::DatabaseType", "QAction", "QWidget", + "KConfigLoader", "KActionCollection") + rule_helpers.module_add_imports(filename, sip, entry, "Plasma/Plasmamod.sip") + +def modulecode(): + return { + "KRunner/KRunner/KRunnermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KScreen.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KScreen.py @@ -0,0 +1,67 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KScreen. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + rule_helpers.module_add_classes(filename, sip, entry, "KScreen::AbstractBackend") + + +def _variable_declash_enum(container, variable, sip, matcher): + sip["enumerations"][0] = sip["enumerations"][0] + " /PyName={}{}/".format(sip["name"], sip["enumerations"][0]) + + +def function_rules(): + return [ + # + # QDebug is not exposed. + # + [".*", "operator<<", ".*", "QDebug.*", "QDebug.*KScreen::.*", rule_helpers.function_discard], + # + # Private constructors. + # + ["KScreen::.*", ".*", ".*", "", ".*Private.*", rule_helpers.function_discard], + ] + + +def variable_rules(): + return [ + # + # Two different enums specify the value "None". + # + ["KScreen::Config", ".*", "enum .*", _variable_declash_enum], + ] + + +def modulecode(): + return { + "KScreen/KScreen/KScreenmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KService.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KService.py @@ -0,0 +1,113 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KService. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def _function_rewrite_using_decl(container, function, sip, matcher): + sip["parameters"] = ["const QString &name"] + sip["fn_result"] = "QVariant" + sip["suffix"] = " const" + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >", + "QList", "QVector") + rule_helpers.module_add_includes(filename, sip, rule, "", "") + + +def container_rules(): + return [ + ["ksycocaentry.h", "KSycocaEntry", ".*", ".*", ".*QSharedData.*", rule_helpers.container_discard_QSharedData_base], + ["kplugininfo.h", "KPluginInfo", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + # + # Provide %MethodCode and a C++ signature. + # + ["KMimeTypeTrader|KServiceTypeTrader", "preferredService", ".*", ".*", ".*", rule_helpers.function_discard], + ["KPluginInfo", "KPluginInfo", ".*", ".*", ".*SharedData.*", rule_helpers.function_discard], + ["KPluginInfo", "service", ".*", ".*", ".*", rule_helpers.function_discard], + ["KService", "service.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KServiceGroup", "root|group|childGroup|addEntry", ".*", ".*", ".*", rule_helpers.function_discard], + ["KServiceType", "parentType|serviceType", ".*", ".*", ".*", rule_helpers.function_discard], + ["KSycoca", "stream", ".*", ".*", ".*", rule_helpers.function_discard], + # + # No KSycocaFactory or KSycocaFactoryList. + # + ["KSycoca", "addFactory|factories", ".*", ".*", ".*", rule_helpers.function_discard], + # + # There is no KServiceOffer. + # + ["KMimeTypeTrader", "filterMimeTypeOffers", ".*", ".*", ".*KServiceOffer.*", rule_helpers.function_discard], + ["KServiceTypeTrader", "weightedOffers", ".*", ".*KServiceOffer.*", ".*", rule_helpers.function_discard], + ["KService", "_k_accessServiceTypes", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Rewrite using declaration. + # + ["KService", "property", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def typedef_rules(): + return [ + # + # There is no KServiceOffer. + # + ["k(mime|service)typetrader.h", "KServiceOfferList", ".*", ".*", rule_helpers.typedef_discard], + ] + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kservicegroup.h::KServiceGroup": { # KServiceGroup : KSycocaEntry + "code": + """ + %ConvertToSubClassCode + + if (dynamic_cast(sipCpp)) + sipClass = sipClass_KServiceGroup; + else if (dynamic_cast(sipCpp)) + sipClass = sipClass_KServiceSeparator; + else + sipClass = NULL; + %End + """ + }, + } + + +def modulecode(): + return { + "KService/KServicemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KSieveUi.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KSieveUi.py @@ -0,0 +1,36 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KSieveUi. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QVector") + + +def modulecode(): + return { + "KSieveUi/KSieveUimod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KStyle.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KStyle.py @@ -0,0 +1,37 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KStyle. This modules describes: + + * Supplementary SIP file generator rules. +""" + +def _function_rewrite_using_decl(container, function, sip, matcher): + sip["parameters"] = ["QApplication *app"] + sip["prefix"] = "virtual " + + +def function_rules(): + return [ + # + # Rewrite using declaration. + # + ["KStyle", "polish", ".*", ".*", "", _function_rewrite_using_decl], + ] + Index: find-modules/module_generation/PyKF5/KTNEF.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KTNEF.py @@ -0,0 +1,44 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KTNEF. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, entry, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", "QLatin1String", "VObject", + "KCalCore::IncidenceBase::VirtualHook", "KCalUtils::InvitationFormatterHelper") + + +def modulecode(): + return { + "KTNEF/KTNEF/KTNEFmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KTextEditor.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KTextEditor.py @@ -0,0 +1,256 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KTextEditor. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def _function_make_public(container, fn, sip, rule): + sip["prefix"] = "public: " + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QSet") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KXmlGuiWindow", "KIconLoader", "KSslCertificateBoxPrivate", + "KIO::Connection", "KIO::ClipboardUpdater") + + +_ktexteditor_qobject_ctscc = """ +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_Document; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_AnnotationModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_Editor; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_LoadSaveFilterCheckPlugin; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_Plugin; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KTextEditor_CodeCompletionModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_CodeCompletionModel2; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_ConfigPage; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_EditorChooser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_View; +%End +""" + + +def container_rules(): + return [ + ["KTextEditor", "Attribute", ".*", ".*", ".*QSharedData.*", rule_helpers.container_discard_QSharedData_base], + ["KTextEditor", "AttributeBlock", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + ["KTextEditor::.*Cursor", "operator Cursor", ".*", ".*", ".*", rule_helpers.function_discard], + ["KTextEditor::MovingRange", "operator Range", ".*", ".*", ".*", rule_helpers.function_discard], + ["KTextEditor::MovingCursor", "MovingCursor", ".*", "", ".*", _function_make_public], + # + # SIP unsupported signal argument type. + # + ["KTextEditor::MarkInterface", "markToolTipRequested", ".*", ".*", ".*", rule_helpers.function_discard], + ["KTextEditor::MarkInterface", "markContextMenuRequested", ".*", ".*", ".*", rule_helpers.function_discard], + ["KTextEditor::MarkInterface", "markClicked", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KTextEditor::AnnotationInterface": { #AnnotationInterface + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'AnnotationInterface' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_AnnotationViewInterface; + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KTextEditor::TemplateInterface": { #TemplateInterface + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'TemplateInterface' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_TemplateInterface2; + %End + """ + }, + "KTextEditor::QHash": { #QHash + "code": + """ + %ConvertFromTypeCode + // Create the dictionary. + PyObject *d = PyDict_New(); + + if (!d) + return NULL; + + // Set the dictionary elements. + QHash::const_iterator i = sipCpp->constBegin(); + + while (i != sipCpp->constEnd()) + { + int t1 = i.key(); + KTextEditor::Mark *t2 = i.value(); + + #if PY_MAJOR_VERSION >= 3 + PyObject *t1obj = PyLong_FromLong ((long)t1); + #else + PyObject *t1obj = PyInt_FromLong ((long)t1); + #endif + PyObject *t2obj = sipConvertFromNewInstance(t2, sipClass_KTextEditor_Mark, sipTransferObj); + + if (t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0) + { + Py_DECREF(d); + + if (t1obj) + Py_DECREF(t1obj); + + if (t2obj) + Py_DECREF(t2obj); + else + delete t2; + + return NULL; + } + + Py_DECREF(t1obj); + Py_DECREF(t2obj); + + ++i; + } + + return d; + %End + %ConvertToTypeCode + PyObject *t1obj, *t2obj; + SIP_SSIZE_T i = 0; + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyDict_Check(sipPy)) + return 0; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + #if PY_MAJOR_VERSION >= 3 + if (!PyNumber_Check (t1obj)) + #else + if (!PyInt_Check (t1obj)) + #endif + return 0; + + if (!sipCanConvertToInstance(t2obj, sipClass_KTextEditor_Mark, SIP_NOT_NONE)) + return 0; + } + + return 1; + } + + QHash *qm = new QHash; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + int state2; + + #if PY_MAJOR_VERSION >= 3 + int t1 = PyLong_AsLong (t1obj); + #else + int t1 = PyInt_AS_LONG (t1obj); + #endif + KTextEditor::Mark *t2 = reinterpret_cast(sipConvertToInstance(t2obj, sipClass_KTextEditor_Mark, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); + + if (*sipIsErr) + { + sipReleaseInstance(t2, sipClass_KTextEditor_Mark, state2); + + delete qm; + return 0; + } + + qm->insert(t1, t2); + + sipReleaseInstance(t2, sipClass_KTextEditor_Mark, state2); + } + + *sipCppPtr = qm; + + return sipGetState(sipTransferObj); + %End + """ + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KTextEditor::Document": { #Document : KParts::ReadWritePart + "code": _ktexteditor_qobject_ctscc + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KTextEditor::CodeCompletionModel": { #CodeCompletionModel : QAbstractItemModel + "code": _ktexteditor_qobject_ctscc + }, + # DISABLED until I figure out an approach for CTSCC. + "DISABLED KTextEditor::Attribute": { #Attribute : QTextCharFormat + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'QTextFormat' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KTextEditor_Attribute; + %End + """ + }, + } + + +def modulecode(): + return { + "KTextEditor/KTextEditor/KTextEditormod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KTextWidgets.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KTextWidgets.py @@ -0,0 +1,40 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KTextWidgets. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + + +def modulecode(): + return { + "KTextWidgets/KTextWidgetsmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KUnitConversion.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KUnitConversion.py @@ -0,0 +1,33 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KUnitConversion. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def function_rules(): + return [ + ["KUnitConversion::Unit", "Unit", ".*", ".*", "KUnitConversion::UnitPrivate.*", rule_helpers.function_discard], + ["KUnitConversion::UnitCategory", "UnitCategory", ".*", ".*", "KUnitConversion::UnitCategoryPrivate.*", rule_helpers.function_discard], + ] + Index: find-modules/module_generation/PyKF5/KWidgetsAddons.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KWidgetsAddons.py @@ -0,0 +1,135 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KWidgetsAddons. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers +import common_typecode +import rules_engine + + +def _delete_duplicate_content(filename, sip, entry): + sip["decl"] = "" + + +def parameter_rewrite_template(container, function, parameter, sip, matcher): + sip["decl"] = "DragObjectFactory factory" + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, rule, "QMap") + rule_helpers.modulecode_delete(filename, sip, rule, "QPair", "QVector") + rule_helpers.module_add_imports(filename, sip, rule, "KConfigCore/KConfigCoremod.sip") + + +def forward_declaration_rules(): + return [ + ["kdatepicker.h", "KDateTable", ".*", rule_helpers.forward_declaration_mark_external], + ] + + +def function_rules(): + return [ + ["KRatingWidget", "ratingChanged|set.*Rating", ".*", ".*", "unsigned.*", rule_helpers.function_discard], + ["KPageDialog", "KPageDialog", ".*", ".*", "KPageDialogPrivate.*", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + ["KDragWidgetDecorator", "setDragObjectFactory", "factory", ".*", ".*", parameter_rewrite_template], + # + # Override the default "parent" rule. + # + ["KMessageBox", ".*", "parent", ".*", ".*", rule_helpers.noop], + ["KMessageBox", "createKMessageBox", "checkboxReturn", ".*", ".*", rule_helpers.parameter_out], + ] + + +def modulecode(): + return { + "KWidgetsAddons/KWidgetsAddonsmod.sip": { + "code": module_fix_mapped_types, + }, + "KWidgetsAddons/KMimeTypeChooserDialog": { + "code": _delete_duplicate_content + }, + "KWidgetsAddons/KMultiTabBarButton": { + "code": _delete_duplicate_content + }, + "KWidgetsAddons/KMultiTabBarTab": { + "code": _delete_duplicate_content + }, + "KWidgetsAddons/KPageWidgetItem": { + "code": _delete_duplicate_content + }, + "KWidgetsAddons/KGradientSelector": { + "code": _delete_duplicate_content + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED kratingwidget.h::KRatingWidget": { + "code": common_typecode._kdeui_qobject_ctscc + }, + } + +def methodcode(): + return { + "KFontChooser": { + "KFontChooser": { + # + # SIP needs help with the semantics of argument 5. + # + "parameters": [ + "QWidget *parent /TransferThis/ = nullptr", + "const QFlags &flags = KFontChooser::DisplayFrame", + "const QStringList &fontList = QStringList()", + "int visibleListSize = 8", + "SIP_PYOBJECT sizeIsRelativeState = nullptr" + ], + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + ::Qt::CheckState *cxxa4 = nullptr; + if (a4 != nullptr) { + int a4Err; + cxxa4 = static_cast(sipForceConvertToType(a4, sipType_Qt_CheckState, NULL, SIP_NOT_NONE, NULL, &a4Err)); + if (a4Err) { + sipError = sipBadCallableArg(4, a4); + return NULL; + } + } + sipCpp = new sipKFontChooser(a0, *a1, *a2, a3, cxxa4); + Py_END_ALLOW_THREADS + %End + """ + }, + }, + } Index: find-modules/module_generation/PyKF5/KWindowSystem.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KWindowSystem.py @@ -0,0 +1,81 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KWindowSystem. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def function_get_array(container, function, sip, rule): + rule_helpers.initialise_cxx_decl(sip) + sip["fn_result"] = "SIP_PYLIST" + sip["code"] += """%MethodCode +// TBD +%End +""" + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList") + rule_helpers.module_add_classes(filename, sip, rule, "_XEvent", "xcb_generic_event_t", "xcb_key_press_event_t", + "xcb_connection_t", "_XDisplay", "xcb_window_t") + + +def module_fix_mapped_types_private(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QList") + rule_helpers.module_add_classes(filename, sip, rule, "_XEvent", "xcb_generic_event_t", "xcb_key_press_event_t", + "xcb_connection_t", "_XDisplay", "xcb_window_t") + + +def container_rules(): + return [ + ["kwindowinfo_p.h", "KWindowInfoPrivate", ".*", ".*", ".*", rule_helpers.container_discard_QSharedData_base], + ["kwindowinfo.h", "KWindowInfo", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + ["KStartupInfo", "KStartupInfo", ".*", ".*", "bool.*", rule_helpers.function_discard], + ["NETRootInfo", "(clientList(|Stacking))|virtualRoots", ".*", ".*", ".*", function_get_array], + ["NETWinInfo", "iconSizes", ".*", ".*", ".*", function_get_array], + # + # SIP unsupported signal argument type. + # + ["KWindowSystem", "windowChanged", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KWindowSystem/KWindowSystemmod.sip": { + "code": module_fix_mapped_types, + }, + "KWindowSystem/private/privatemod.sip": { + "code": module_fix_mapped_types_private, + }, + } Index: find-modules/module_generation/PyKF5/KXmlGui.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KXmlGui.py @@ -0,0 +1,107 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KXmlGui. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def _parameter_remove_qualifier(container, function, parameter, sip, matcher): + sip["init"] = sip["init"].split(":")[-1] + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QList") + rule_helpers.module_add_imports(filename, sip, rule, "QtXml/QtXmlmod.sip") + + +def function_rules(): + return [ + ["KMainWindow", "k_func", ".*", ".*", ".*", ".*", ".*", rule_helpers.function_discard], + ["KMainWindow", "KMainWindow", ".*", ".*", "KMainWindowPrivate.*", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + ["KMainWindow", "KMainWindow", "f", ".*", ".*::.*", _parameter_remove_qualifier], + ] + + +def modulecode(): + return { + "KXmlGui/kgesturemap_p.h": { + "code": "", + "decl": "" + }, + "KXmlGui/KToggleToolBarAction": { + "code": "", + "decl": "" + }, + # + # No multiple inheritance. + # + "KXmlGui/KXmlGuiWindow": { + "code": "", + "decl": "" + }, + "KXmlGui/KXmlGuimod.sip": { + "code": module_fix_mapped_types, + }, + } + + +def methodcode(): + return { + "KXMLGUIBuilder": { + "createContainer": { + "parameters": ["QWidget* parent /Transfer/", "int index", "const QDomElement& element"], + "fn_result": "SIP_PYTUPLE", + "cxx_parameters": ["QWidget* parent", "int index", "const QDomElement& element", "QAction*& containerAction"], + "cxx_fn_result": "QWidget*", + "code": + """ + %MethodCode + QAction *containerAction; + QWidget* res; + Py_BEGIN_ALLOW_THREADS + res = sipSelfWasArg ? sipCpp->KXMLGUIBuilder::createContainer (a0, a1, *a2, containerAction) : sipCpp->createContainer (a0, a1, *a2, containerAction); + Py_END_ALLOW_THREADS + + PyObject *pyWidget; + PyObject *pyContainerAction; + + if ((pyWidget = sipConvertFromNewInstance(res, sipClass_QWidget, NULL)) == NULL) + return NULL; + + if ((pyContainerAction = sipConvertFromNewInstance(containerAction, sipClass_QAction, NULL)) == NULL) + return NULL; + + sipRes = Py_BuildValue ("NN", pyWidget, pyContainerAction); + %End + """ + }, + }, + } Index: find-modules/module_generation/PyKF5/Kaddressbook.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Kaddressbook.py @@ -0,0 +1,44 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KaddressbookGrantlee. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Connection", "KService", "KIO::ClipboardUpdater", + "KPIMTextEdit::PlainTextEditor", "PimCommon::PimCommonSettingsBase", + "KActionCollection", "KPIMTextEdit::NestedListHelper", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", "KIMAP::Message", + "KUrlRequester", "QScriptEngine", "OrgKdeAkonadiImapSettingsInterface", + "KXMLGUIClient", "KPIM::ProgressItem", "KWallet::Wallet", + "PimCommon::StorageServiceTreeWidgetItem", "Akonadi::AbstractContactEditorWidget", + "KLocalizedString", "GrantleeTheme::Theme") + + +def modulecode(): + return { + "KaddressbookGrantlee/KaddressbookGrantleemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/KdepimDBusInterfaces.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KdepimDBusInterfaces.py @@ -0,0 +1,37 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KdepimDBusInterfaces. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "KConfigGroup", "KCoreConfigSkeleton", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def modulecode(): + return { + "KdepimDBusInterfaces/KdepimDBusInterfacesmod.sip": { + "code": module_fix_mapped_types + }, + } Index: find-modules/module_generation/PyKF5/KontactInterface.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/KontactInterface.py @@ -0,0 +1,40 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.KontactInterface. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList") + + +def modulecode(): + return { + "KontactInterface/KontactInterface/KontactInterfacemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/Kross.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Kross.py @@ -0,0 +1,73 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Kross. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def container_fixup(container, sip, rule): + rule_helpers.container_discard_QSharedData_base(container, sip, rule) + rule_helpers.container_make_unassignable(container, sip, rule) + + +def module_fix_mapped_types_core(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_make_local(filename, sip, rule, "QList", "QMap") + rule_helpers.module_add_imports(filename, sip, rule, "SonnetCore/Sonnet/Sonnetmod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "QScriptable") + + +def module_fix_mapped_types_ui(filename, sip, rule): + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KXmlGuiWindow", "QScriptable", "KIO::Connection", + "KIO::ClipboardUpdater", "KIconLoader") + + +def container_rules(): + return [ + # + # SIP does not seem to be able to handle these type specialization, but we can live without them? + # + ["Kross", "MetaTypeVariant", "", ".*", ".*", rule_helpers.container_discard], + ["Kross", "Object", ".*", ".*", ".*", container_fixup], + ["Kross", "InterpreterInfo", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + ["Kross::Manager", "registerMetaTypeHandler", ".*", ".*", ".*", rule_helpers.function_discard], + ["Kross::MetaTypeHandler", "MetaTypeHandler", ".*", ".*", ".*func", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "KrossCore/Kross/Core/Coremod.sip": { + "code": module_fix_mapped_types_core, + }, + "KrossUi/Kross/Ui/Uimod.sip": { + "code": module_fix_mapped_types_ui, + }, + } Index: find-modules/module_generation/PyKF5/Libkdepim.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Libkdepim.py @@ -0,0 +1,43 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Libkdepim. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QVector", "QVector") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KSycoca::DatabaseType", "KSslCertificateBoxPrivate", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", "KIO::Connection", + "KIO::ClipboardUpdater") + +def modulecode(): + return { + "Libkdepim/Libkdepimmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/Libkleo.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Libkleo.py @@ -0,0 +1,65 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Libkleo. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList", "QMap", + "std::vector >", + "std::vector >", + "std::vector >", + "std::vector >", + "std::vector >") + rule_helpers.module_add_classes(filename, sip, rule, "KConfig", "_IO_FILE", "Kleo::DownloadJob", + "Kleo::RefreshKeysJob") + + +def container_rules(): + return [ + # + # We cannot handle templated containers which are this complicated. + # + ["Kleo::_detail", "ByFingerprint|ByKeyID|ByShortKeyID|ByChainID", ".*", ".*", ".*", rule_helpers.container_discard], + ["kdtools", "select1st|select2nd", ".*", ".*", ".*", rule_helpers.container_discard], + ["Kleo::KeyApprovalDialog", "Item", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def modulecode(): + return { + "Libkleo/CryptoBackend": { + "code": rule_helpers.module_yank_scoped_class, + "ctx": {"child": "Protocol", "parent": "CryptoBackend"}, + }, + "Libkleo/Dn": { + "code": rule_helpers.module_yank_scoped_class, + "ctx": {"child": "Attribute", "parent": "DN"}, + }, + "Libkleo/Libkleomod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MailCommon.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MailCommon.py @@ -0,0 +1,72 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MailCommon. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def function_make_callable(container, fn, sip, rule): + sip["parameters"][-1] = "SIP_PYCALLABLE ignoreCollectionCallback = 0" + + +def parameter_fix_default(container, fn, parameter, sip, rule): + lhs, rhs = sip["init"].split(")", 1) + sip["init"] = "QFlags<" + lhs[:-1] + ">" + rhs + ")" + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QExplicitlySharedDataPointer", + "QMap", "QVector", + "QVector", "QVector", + "QVector") + rule_helpers.module_add_classes(filename, sip, rule, "KIO::Job", "MessageCore::MessageCoreSettingsBase", + "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", "KTimeZoneSource", + "icalcomponent_impl", "_icaltimezone", "KDateTime", "KDateTime::Spec", + "VObject", "MessageViewer::MessageViewerSettingsBase", "_IO_FILE", + "Kleo::DownloadJob", "Kleo::RefreshKeysJob", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate") + + +def function_rules(): + return [ + ["MailCommon::Util", "nextUnreadCollection", ".*", ".*", ".*", function_make_callable], + ] + + +def parameter_rules(): + return [ + ["MailCommon::FolderTreeWidget", "FolderTreeWidget", "options", ".*", ".*", parameter_fix_default], + ["MailCommon::SearchPatternEdit", "SearchPatternEdit", "options", ".*", ".*", parameter_fix_default], + ["MailCommon::SearchRuleWidget", "SearchRuleWidget", "options", ".*", ".*", parameter_fix_default], + ["MailCommon::SearchRuleWidgetLister", "SearchRuleWidgetLister", "opt", ".*", ".*", parameter_fix_default], + ] + + +def modulecode(): + return { + "MailCommon/MailCommonmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MailImporter.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MailImporter.py @@ -0,0 +1,60 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MailImporter. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def _function_avoid_keyword(container, function, sip, rule): + sip["annotations"].add("PyName=import_") + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QSharedPointer") + rule_helpers.module_delete_imports(filename, sip, rule, "Akonadi/KMime/KMimemod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", + "Akonadi::SpecialMailCollectionsPrivate", "KLocalizedString", "KConfigGroup", + "KCoreConfigSkeleton", "Akonadi::MessageStatus") + + +def container_rules(): + return [ + ["MailImporter", "FolderStructureBase", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + ["MailImporter::FilterPMail", "import", ".*", ".*", ".*", _function_avoid_keyword], + ["MailImporter::FilterPMail", "processFiles", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "MailImporter/MailImportermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MailTransport.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MailTransport.py @@ -0,0 +1,55 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MailTransport. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def _parameter_remove_prefix(container, function, parameter, sip, matcher): + sip["init"] = sip["init"].replace("Akonadi::-1", "-1") + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QSharedPointer") + rule_helpers.module_add_classes(filename, sip, entry, "Akonadi::SpecialMailCollectionsPrivate", + "MailTransport::SentActionAttribute", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate", "KWallet::Wallet", "KMime::Message") + rule_helpers.module_add_imports(filename, sip, entry, "MailTransport/mailtransport/mailtransportmod.sip") + rule_helpers.module_delete_imports(filename, sip, entry, "KMime/KMime/KMimemod.sip") + + +def parameter_rules(): + return [ + ["MailTransport::SentBehaviourAttribute", "SentBehaviourAttribute", "moveToCollection", ".*", ".*", _parameter_remove_prefix], + ] + + +def modulecode(): + return { + "MailTransport/MailTransport/MailTransportmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MessageComposer.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MessageComposer.py @@ -0,0 +1,87 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MessageComposer. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def container_not_abstract(container, sip, rule): + sip["annotations"].remove("Abstract") + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QExplicitlySharedDataPointer", "QList", + "QList", "QList >", + "QList >", "QList", + "QMap", "QSharedPointer", + "QSharedPointer", + "QSharedPointer", "QVector", + "QVector", "QVector", + "QVector", "QVector", + "std::vector >") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KPIMTextEdit::PlainTextEditor", + "MessageCore::MessageCoreSettingsBase", "KTimeZone", "KTimeZoneBackend", + "KTimeZoneData", "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", + "KDateTime", "KDateTime::Spec", "MessageViewer::MessageViewerSettingsBase", + "MessageComposer::ContentJobBase", "MessageComposer::AbstractEncryptJob", + "MessageComposer::MessageComposerSettingsBase", "_IO_FILE", "Kleo::DownloadJob", + "Kleo::RefreshKeysJob", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate", "KIO::Connection", "KIO::ClipboardUpdater", + "KParts::ReadOnlyPart", "QLatin1String", "VObject", "KActionCollection", + "Akonadi::SpecialMailCollectionsPrivate", "MessageViewer::ViewerPrivate", + "KLocalizedString", "KPIMTextEdit::NestedListHelper", + "MailTransport::TransportComboBox", "SendLater::SendLaterInfo", + "MessageComposer::RecipientsPicker", "PimCommon::AutoCorrection", + "MailTransport::MessageQueueJob") + + +def container_rules(): + return [ + # + # There is a stray inlined destructor which confuses things... + # + ["messagesender.h", "MessageComposer", ".*", ".*", ".*", container_not_abstract], + ["MessageComposer", "MessageComposerSettings", ".*", ".*", ".*", rule_helpers.container_make_uncopyable], + ] + + +def variable_rules(): + return [ + # + # SIP does not support global arrays: + # + # https://www.riverbankcomputing.com/pipermail/pyqt/2017-September/039553.html + # + # ["kleo_util.h", "c(oncreteC|)ryptoMessageFormats", ".*", rule_helpers.variable_discard], + ] + + +def modulecode(): + return { + "MessageComposer/MessageComposermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MessageCore.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MessageCore.py @@ -0,0 +1,65 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MessageCore. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QSharedPointer", "QList", + "QVector", "QVector") + + rule_helpers.module_delete_imports(filename, sip, rule, "Akonadi/KMime/KMimemod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "MessageCore::MessageCoreSettingsBase", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", + "Akonadi::SpecialMailCollectionsPrivate", "KLocalizedString", "KConfigGroup", + "KCoreConfigSkeleton", "Akonadi::MessageStatus") + rule_helpers.modulecode_make_local(filename, sip, rule, "QList") + + +def container_rules(): + return [ + ["MessageCore", "MessageCoreSettings", ".*", ".*", ".*", rule_helpers.container_make_uncopyable] + ] + + +def parameter_rules(): + return [ + ["MessageCore::AttachmentFromUrlUtils", "createAttachmentJob", "parent", ".*", ".*", rule_helpers.noop] + ] + + +def typedef_rules(): + return [ + ["KMime::Types", "AddressList", ".*", ".*", rule_helpers.typedef_discard], + ] + + +def modulecode(): + return { + "MessageCore/MessageCoremod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MessageList.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MessageList.py @@ -0,0 +1,46 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MessageList. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QVector", "QList", + "QVector", "QSharedPointer", + "QVector") + rule_helpers.module_add_classes(filename, sip, entry, "Akonadi::SpecialMailCollectionsPrivate", "KLocalizedString", + "KXMLGUIClient", "MessageList::Core::SearchLineStatus", + "MessageList::Core::MessageItem", "MessageList::Core::Widget", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def modulecode(): + return { + "MessageList/MessageListmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/MessageViewer.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/MessageViewer.py @@ -0,0 +1,188 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.MessageViewer. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + +# +# For these two types, SIP generate %MappedType filenames which are too long +# for the filesystem: +# +# "typedef std::multimap > > SubtypeRegistry;" +# "typedef std::map > >, MessageViewer::ltstr, " +# "std::allocator > > > > > TypeRegistry; +# +# The following typedefs are used to shorten the names. (Notice that the logic +# in container_add_typedefs would create SIP class definitions which clashed +# with the %MappedType names if the whole type were typedef'd). +# +TYPEDEF_0 = "__MessageViewer0_t" +TYPEDEF_1 = "__MessageViewer1_t" +TYPEDEF_2 = "__MessageViewer2_t" +FORMATTER = "MessageViewer::Interface::BodyPartFormatter" +ALLOCATOR_1 = "std::allocator >" \ + .replace(FORMATTER, TYPEDEF_0) +ALLOCATOR_2 = "std::allocator > > > >" \ + .replace(FORMATTER, TYPEDEF_0).replace(ALLOCATOR_1, TYPEDEF_1) +MULTIMAP = "std::multimap > >" \ + .replace(FORMATTER, TYPEDEF_0).replace(ALLOCATOR_1, TYPEDEF_1) +MAP = "std::map > >, MessageViewer::ltstr, " \ + "std::allocator > > > > >" \ + .replace(FORMATTER, TYPEDEF_0).replace(ALLOCATOR_1, TYPEDEF_1).replace(ALLOCATOR_2, TYPEDEF_2) + +def container_add_typedefs_to_shorten_name(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, FORMATTER, ALLOCATOR_1, ALLOCATOR_2) + + +def function_pass_callable_ia(container, fn, sip, rule): + sip["code"] = """ +%MethodCode + // Make sure the callable doesn't get garbage collected. + Py_INCREF(a0); + Py_BEGIN_ALLOW_THREADS + sipCpp->injectAttachments([a0, &sipIsErr]()->QString{ + PyObject *innerResult; + QString *innerCxxResult; + SIP_BLOCK_THREADS + innerResult = sipCallMethod(NULL, a0, ""); + Py_DECREF(a0); + if (!innerResult) { + PyErr_SetString(PyExc_TypeError, "invalid result from injectAttachments"); + } else { + innerCxxResult = reinterpret_cast(sipConvertToType(innerResult, sipType_QString, NULL, SIP_NO_CONVERTORS, 0, &sipIsErr)); + Py_DECREF(innerResult); + } + SIP_UNBLOCK_THREADS + return *innerCxxResult; + }); + Py_END_ALLOW_THREADS +%End +""" + + +def function_pass_callable_rih(container, fn, sip, rule): + sip["code"] = """ +%MethodCode + // Make sure the callable doesn't get garbage collected. + Py_INCREF(a1); + Py_BEGIN_ALLOW_THREADS + sipRes = sipCpp->replaceInnerHtml(*a0, [a1, &sipIsErr]()->QString{ + PyObject *innerResult; + QString *innerCxxResult; + SIP_BLOCK_THREADS + innerResult = sipCallMethod(NULL, a1, ""); + Py_DECREF(a1); + if (!innerResult) { + PyErr_SetString(PyExc_TypeError, "invalid result from replaceInnerHtml"); + } else { + innerCxxResult = reinterpret_cast(sipConvertToType(innerResult, sipType_QString, NULL, SIP_NO_CONVERTORS, 0, &sipIsErr)); + Py_DECREF(innerResult); + } + SIP_UNBLOCK_THREADS + return *innerCxxResult; + }); + Py_END_ALLOW_THREADS +%End +""" + + +def parameter_array_to_star(container, fn, parameter, sip, rule): + sip["decl"] = "const char **" + sip["name"] + + +def parameter_callable(container, fn, parameter, sip, rule): + sip["decl"] = "const SIP_PYCALLABLE &" + sip["name"] + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QExplicitlySharedDataPointer", + "QHash", "QList", "QSharedPointer", + "QSharedPointer", "QSharedPointer", + "QVector", "QVector", + "QVector") + rule_helpers.module_add_imports(filename, sip, rule, "QtDBus/QtDBusmod.sip", "KIOCore/kio/kiomod.sip") + rule_helpers.module_delete_imports(filename, sip, rule, "Akonadi/KMime/KMimemod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KTimeZone", "KTimeZoneBackend", "KTimeZoneData", + "KTimeZoneSource", "icalcomponent_impl", "_icaltimezone", "KDateTime", + "KDateTime::Spec", "VObject", "MessageViewer::MessageViewerSettingsBase", + "_IO_FILE", "Kleo::DownloadJob", "Kleo::RefreshKeysJob", + "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate", "KFileItemList", + "KIO::Connection", "KIO::ClipboardUpdater", "KRemoteEncoding", + "KParts::ReadOnlyPart", "QLatin1String", "KActionCollection", + "Akonadi::SpecialMailCollectionsPrivate", "KLocalizedString", + "MessageViewer::ViewerPrivate", "Akonadi::MessageStatus") + dummy = """%MappedType {type} +{ +%ConvertFromTypeCode +// TBD +%End +%ConvertToTypeCode +// TBD +%End +}; + +""" + sip["modulecode"][MULTIMAP] = dummy.replace("{type}", MULTIMAP) + sip["modulecode"][MAP] = dummy.replace("{type}", MAP) + + +def container_rules(): + return [ + ["MessageViewer", "MessageViewerSettings|FileHtmlWriter", ".*", ".*", ".*", rule_helpers.container_make_uncopyable], + ["bodypartformatterbasefactory.h", "MessageViewer", ".*", ".*", ".*", container_add_typedefs_to_shorten_name], + ] + + +def function_rules(): + return [ + ["MessageViewer::MailWebView", "injectAttachments", ".*", ".*", ".*", function_pass_callable_ia], + ["MessageViewer::MailWebView", "replaceInnerHtml", ".*", ".*", ".*", function_pass_callable_rih], + ] + + +def parameter_rules(): + return [ + # + # [] -> * + # + ["MessageViewer::HeaderStrategy", "stringList", "headers", ".*", ".*", parameter_array_to_star], + ["MessageViewer::MailWebView", "injectAttachments|replaceInnerHtml", "delayedHtml", ".*", ".*", parameter_callable], + ["MessageViewer::Util", "createAppAction", "parent", ".*", ".*", rule_helpers.noop], + ] + + +def modulecode(): + return { + "MessageViewer/MessageViewermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/NetworkManagerQt.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/NetworkManagerQt.py @@ -0,0 +1,54 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.NetworkManagerQt. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QList >") + rule_helpers.module_add_classes(filename, sip, rule, "NetworkManager::ActiveConnectionPrivate", + "NetworkManager::DevicePrivate", "NetworkManager::ModemDevicePrivate", + "NMBluetoothCapabilities") + + +def container_rules(): + return [ + ["NetworkManager", "IpConfig", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def function_rules(): + return [ + ["NetworkManager::VpnConnection", "operator.*\*", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "NetworkManagerQt/NetworkManagerQt/NetworkManagerQtmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/PimCommon.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/PimCommon.py @@ -0,0 +1,71 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.PimCommon. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def container_add_typedefs(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "QMap >") + + +def parameter_remove_default(container, fn, parameter, sip, rule): + sip["init"] = "" + + +def module_fix_mapped_types(filename, sip, rule): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, rule, "QHash", "QList", + "QMap >", "QVector", + "QVector") + rule_helpers.module_add_imports(filename, sip, rule, "KIOCore/kio/kiomod.sip") + rule_helpers.module_add_classes(filename, sip, rule, "KPIMTextEdit::PlainTextEditor", + "PimCommon::PimCommonSettingsBase", "KActionCollection", + "KPIMTextEdit::NestedListHelper", "KIO::Connection", "KService", + "KIO::ClipboardUpdater", "Akonadi::Protocol::Command", + "Akonadi::ServerManagerPrivate", "KIMAP::Message", "KUrlRequester", "QScriptEngine", + "OrgKdeAkonadiImapSettingsInterface", "KXMLGUIClient", "KPIM::ProgressItem", + "KWallet::Wallet", "PimCommon::StorageServiceTreeWidgetItem") + + +def container_rules(): + return [ + ["PimCommon", "ImapAclAttribute", ".*", ".*", ".*", container_add_typedefs], + ["PimCommon", "Translator(|Result)TextEdit", ".*", ".*", ".*", rule_helpers.container_make_uncopyable], + ["PimCommon", "PimCommonSettings", ".*", ".*", ".*", rule_helpers.container_make_uncopyable], + ] + + +def parameter_rules(): + return [ + ["PimCommon::StorageServiceSettingsWidget", "setListService", "lstCap", ".*", ".*", parameter_remove_default], + ] + + +def modulecode(): + return { + "PimCommon/PimCommonmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/Plasma.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Plasma.py @@ -0,0 +1,252 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Plasma. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + +_plasma_qobject_ctscc = """ +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ConfigLoader; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AccessAppletJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ServiceAccessJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ServiceJob; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AbstractDialogManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AbstractRunner; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AccessManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AnimationDriver; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Animator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AuthorizationManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AuthorizationRule; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ClientPinRequest; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ContainmentActions; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Context; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_DataContainer; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_DataEngine; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_DataEngineManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_PackageStructure; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_RunnerContext; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_RunnerManager; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Plasma_ScriptEngine; + if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AppletScript; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_DataEngineScript; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_RunnerScript; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_WallpaperScript; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Service; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Plasma_Svg; + if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_FrameSvg; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Theme; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ToolTipManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Wallpaper; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Animation; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Delegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Corona; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AbstractToolBox; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Plasma_Applet; + if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_AppletProtectedThunk; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Containment; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_GLApplet; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_PopupApplet; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_BusyWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_DeclarativeWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Extender; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Plasma_ExtenderItem; + if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ExtenderGroup; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_FlashingLabel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Frame; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_IconWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ItemBackground; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Meter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ScrollWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Separator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_SignalPlotter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_SvgWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_TabBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_WebView; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_CheckBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_GroupBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Label; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_LineEdit; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_PushButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_RadioButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ScrollBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Slider; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_SpinBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_TextBrowser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_TextEdit; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_ToolButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_TreeView; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_VideoWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_Dialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Plasma_View; +%End +""" + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QList", + "QList", "QList", "QList", + "QMap") + rule_helpers.module_add_classes(filename, sip, entry, "KConfigLoader", "KActionCollection") + rule_helpers.module_add_imports(filename, sip, entry, "QtWidgets/QtWidgetsmod.sip") + + +def modulecode(): + return { + "Plasma/Plasmamod.sip": { + "code": module_fix_mapped_types, + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Plasma::AbstractRunner": { + "code": _plasma_qobject_ctscc + }, + # ./plasma/animation.sip + "Animation": { # Animation : QAbstractAnimation + "code": _plasma_qobject_ctscc + }, + # ./plasma/packagestructure.sip + "QList": { # QList + "code": + """ + %ConvertToTypeCode + return NULL; + %End + %ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + for (int i = 0; i < sipCpp->size(); ++i) + { + PyObject *pobj; + int iserr; + + if ((pobj = sipBuildResult(&iserr,"s",sipCpp->value(i))) == NULL) + { + Py_DECREF(l); + + return NULL; + } + + PyList_SET_ITEM(l, i, pobj); + } + + return l; + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/SendLater.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/SendLater.py @@ -0,0 +1,37 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.SendLater. This modules describes: + + * Supplementary SIP file generator rules. +""" + +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + rule_helpers.module_add_classes(filename, sip, entry, "Akonadi::Protocol::Command", "Akonadi::ServerManagerPrivate") + + +def modulecode(): + return { + "SendLater/SendLatermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/Solid.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Solid.py @@ -0,0 +1,268 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Solid. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", "QMap") + + +def function_rules(): + return [ + # + # Discard non-const. + # + ["Solid::Device", "asDeviceInterface", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ] + + +def modulecode(): + return { + "Solid/Solid/Solidmod.sip": { + "code": module_fix_mapped_types, + }, + } + + +def typecode(): + return { + # DISABLED until I figure out an approach for CTSCC. + "DISABLED Solid::DeviceInterface": { # DeviceInterface : QObject + "code": + """ + %ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + { + sipType = sipType_Solid_DeviceInterface; + if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_AcAdapter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_AudioInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Battery; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Block; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Button; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Camera; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_DvbInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_GenericInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_InternetGateway; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_NetworkInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_NetworkShare; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_PortableMediaPlayer; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Processor; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_SerialInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_SmartCardReader; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_StorageAccess; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Solid_StorageDrive; + if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_OpticalDrive; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Solid_StorageVolume; + if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_OpticalDisc; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Video; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_DeviceNotifier; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Solid_Networking_Notifier; + %End + """ + }, + # ./solid/powermanagement.sip + "QSet": { # QSet + "code": + """ + %TypeHeaderCode + #include + #include + %End + %ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + QSet set = *sipCpp; + int i = 0; + foreach (Solid::PowerManagement::SleepState value, set) + { + #if PY_MAJOR_VERSION >= 3 + PyObject *obj = PyLong_FromLong ((long) value); + #else + PyObject *obj = PyInt_FromLong ((long) value); + #endif + if (obj == NULL || PyList_SET_ITEM (l, i, obj) < 0) + { + Py_DECREF(l); + + if (obj) + Py_DECREF(obj); + + return NULL; + } + + Py_DECREF(obj); + i++; + } + + return l; + %End + %ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + QSet *qs = new QSet; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + #if PY_MAJOR_VERSION >= 3 + Solid::PowerManagement::SleepState t = (Solid::PowerManagement::SleepState)PyLong_AsLong (PyList_GET_ITEM (sipPy, i)); + #else + Solid::PowerManagement::SleepState t = (Solid::PowerManagement::SleepState)PyInt_AS_LONG (PyList_GET_ITEM (sipPy, i)); + #endif + *qs << t; + + } + + *sipCppPtr = qs; + + return sipGetState(sipTransferObj); + %End + """ + }, + # ./solid/predicate.sip + "QSet": { # QSet + "code": + """ + %TypeHeaderCode + #include + #include + %End + %ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + QSet set = *sipCpp; + int i = 0; + foreach (Solid::DeviceInterface::Type value, set) + { + #if PY_MAJOR_VERSION >= 3 + PyObject *obj = PyLong_FromLong ((long) value); + #else + PyObject *obj = PyInt_FromLong ((long) value); + #endif + if (obj == NULL || PyList_SET_ITEM (l, i, obj) < 0) + { + Py_DECREF(l); + + if (obj) + Py_DECREF(obj); + + return NULL; + } + + Py_DECREF(obj); + i++; + } + + return l; + %End + %ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + QSet *qs = new QSet; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + #if PY_MAJOR_VERSION >= 3 + Solid::DeviceInterface::Type t = (Solid::DeviceInterface::Type)PyLong_AsLong (PyList_GET_ITEM (sipPy, i)); + #else + Solid::DeviceInterface::Type t = (Solid::DeviceInterface::Type)PyInt_AS_LONG (PyList_GET_ITEM (sipPy, i)); + #endif + *qs << t; + + } + + *sipCppPtr = qs; + + return sipGetState(sipTransferObj); + %End + """ + }, + } Index: find-modules/module_generation/PyKF5/Sonnet.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Sonnet.py @@ -0,0 +1,47 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Sonnet*. This modules describes: + + * Supplementary SIP file generator rules. +""" + +def _function_rewrite_using_decl(container, function, sip, matcher): + sip["parameters"] = ["int"] + sip["prefix"] = "virtual " + + +def function_rules(): + return [ + # + # Rewrite using declaration. + # + ["Sonnet::Dialog", "done", ".*", ".*", "", _function_rewrite_using_decl], + ] + + +def modulecode(): + return { + "SonnetUi/Sonnet/Dialog": { + "code": + """ + class Sonnet::BackgroundChecker /External/; + """ + }, + } Index: find-modules/module_generation/PyKF5/Syndication.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/Syndication.py @@ -0,0 +1,124 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.Syndication. This modules describes: + + * Supplementary SIP file generator rules. +""" +import os + +import rule_helpers +from templates import PyQt + + +def typedef_duplicate_discard(container, typedef, sip, matcher): + """ + There are multiple definitions like this: + + typedef QSharedPointer CategoryPtr; + + We need to get rid of each copy not in the canonical file. + """ + pointer = os.path.basename(container.translation_unit.spelling) + pointer = os.path.splitext(pointer)[0] + pointer = pointer.capitalize() + "Ptr" + if pointer != typedef.spelling: + rule_helpers.typedef_discard(container, typedef, sip, matcher) + else: + # + # This is the one we want. Keep it, and generate its %MappedType. + # + PyQt.pointer_typecode(container, typedef, sip, matcher) + + + +def module_fix_mapped_types(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer") + rule_helpers.module_add_imports(filename, sip, entry, "QtXml/QtXmlmod.sip", + "Syndication/Syndication/Rss2/Rss2mod.sip", + "Syndication/Syndication/Rdf/Rdfmod.sip", + "Syndication/Syndication/Atom/Atommod.sip") + + +def module_fix_mapped_types_atom(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", + "QSharedPointer") + rule_helpers.module_add_imports(filename, sip, entry, "QtXml/QtXmlmod.sip", + "Syndication/Syndication/Rss2/Rss2mod.sip", + "Syndication/Syndication/Rdf/Rdfmod.sip") + + +def module_fix_mapped_types_rdf(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QSharedPointer") + rule_helpers.module_add_imports(filename, sip, entry, "QtXml/QtXmlmod.sip", + "Syndication/Syndication/Rss2/Rss2mod.sip", + "Syndication/Syndication/Atom/Atommod.sip") + + +def module_fix_mapped_types_rss2(filename, sip, entry): + # + # SIP cannot handle duplicate %MappedTypes. + # + rule_helpers.modulecode_delete(filename, sip, entry, "QList", + "QSharedPointer") + rule_helpers.module_add_imports(filename, sip, entry, "QtXml/QtXmlmod.sip", + "Syndication/Syndication/Rdf/Rdfmod.sip", + "Syndication/Syndication/Atom/Atommod.sip") + + +def function_rules(): + return [ + ["Syndication.*", "operator QString", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Provide %MethodCode and a C++ signature. + # + ["Syndication", "parserCollection", ".*", ".*", ".*", rule_helpers.function_discard], + ] + + +def typedef_rules(): + return [ + ["Syndication.*", ".*Ptr", ".*", "QSharedPointer", typedef_duplicate_discard], + ] + + +def modulecode(): + return { + "Syndication/Syndication/Atom/Atommod.sip": { + "code": module_fix_mapped_types_atom, + }, + "Syndication/Syndication/Rdf/Rdfmod.sip": { + "code": module_fix_mapped_types_rdf, + }, + "Syndication/Syndication/Rss2/Rss2mod.sip": { + "code": module_fix_mapped_types_rss2, + }, + "Syndication/Syndication/Syndicationmod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/TemplateParser.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/TemplateParser.py @@ -0,0 +1,36 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.TemplateParser. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.modulecode_delete(filename, sip, rule, "QSharedPointer") + + +def modulecode(): + return { + "TemplateParser/TemplateParsermod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/XsltKde.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/XsltKde.py @@ -0,0 +1,36 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.XsltKde. This modules describes: + + * Supplementary SIP file generator rules. +""" +import rule_helpers + + +def module_fix_mapped_types(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "QMatrix") + + +def modulecode(): + return { + "XsltKde/XsltKdemod.sip": { + "code": module_fix_mapped_types, + }, + } Index: find-modules/module_generation/PyKF5/__init__.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/__init__.py @@ -0,0 +1,470 @@ +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +SIP binding customisation for PyKF5. This modules describes: + + * The SIP file generator rules. + + * The SIP compilation rules. + +""" +import os +import re +import sys +from clang.cindex import AccessSpecifier +from importlib import import_module + +import common_methodcode +import common_modulecode +import common_typecode +import rule_helpers +import rules_engine +import templates.PyQt +import templates.std_n_boost +from clangcparser import CursorKind + + +def _function_discard_class(container, fn, sip, matcher): + sip["fn_result"] = sip["fn_result"].replace("class ", "") + + +def _function_discard_non_const(container, fn, sip, matcher): + if not sip["suffix"]: + rule_helpers.function_discard(container, fn, sip, matcher) + + +def _function_discard_protected(container, fn, sip, matcher): + if fn.access_specifier == AccessSpecifier.PROTECTED: + rule_helpers.function_discard(container, fn, sip, matcher) + + +def _parameter_rewrite_without_colons(container, fn, parameter, sip, matcher): + sip["decl"] = sip["decl"].replace("::", "") + + +def _parameter_transfer_to_parent(container, fn, parameter, sip, matcher): + if fn.kind != CursorKind.CONSTRUCTOR and \ + not fn.spelling.startswith("create"): + # + # This does not look like a constructor or a factory method. + # + return rule_helpers.SILENT_NOOP + if fn.is_static_method(): + sip["annotations"].add("Transfer") + else: + sip["annotations"].add("TransferThis") + + +def _parameter_set_max_int(container, fn, parameter, sip, matcher): + sip["init"] = "(uint)-1" + + +def _parameter_strip_class_enum(container, fn, parameter, sip, matcher): + sip["decl"] = sip["decl"].replace("class ", "").replace("enum ", "") + + +def _typedef_discard(container, typedef, sip, matcher): + sip["name"] = "" + + +def _typedef_rewrite_as_int(container, typedef, sip, matcher): + sip["decl"] = "int" + + +def _typedef_rewrite_without_colons(container, typedef, sip, matcher): + sip["decl"] = sip["decl"].strip(":") + + +def _variable_discard_protected(container, variable, sip, matcher): + if variable.access_specifier in [AccessSpecifier.PROTECTED, AccessSpecifier.PRIVATE]: + rule_helpers.variable_discard(container, variable, sip, matcher) + + +def container_rules(): + return [ + # + # Discard Qt metatype system. + # + [".*", "(QMetaTypeId|QTypeInfo)", ".*", ".*", ".*", rule_helpers.container_discard], + # + # SIP cannot handle templated containers with a base which is a template parameter. + # + ["kimagecache.h", "KSharedPixmapCacheMixin", ".+", ".*", ".*", rule_helpers.container_discard], + # + # SIP cannot handle inline templates like "class Foo: Bar" without an intermediate typedef. For now, + # delete the base class. + # + [".*", ".*", ".*", ".*", ".*<.*", rule_helpers.container_discard_templated_bases], + # + # SIP does not seem to be able to handle empty containers. + # + ["KParts::ScriptableExtension", "Null|Undefined", ".*", ".*", ".*", rule_helpers.container_discard], + # + # This is pretty much a disaster area. TODO: can we rescue some parts? + # + [".*", "KConfigCompilerSignallingItem", ".*", ".*", ".*", rule_helpers.container_discard], + ["ConversionCheck", ".*", ".*", ".*", ".*", rule_helpers.container_discard], + ] + + +def function_rules(): + return [ + # + # Discard functions emitted by QOBJECT. + # + [".*", "metaObject|qt_metacast|tr|trUtf8|qt_metacall|qt_check_for_QOBJECT_macro", ".*", ".*", ".*", rule_helpers.function_discard], + [".*", "d_func", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP does not support operator=. + # + [".*", "operator=", ".*", ".*", ".*", rule_helpers.function_discard], + # + # TODO: Temporarily remove any functions which require templates. + # + [".*", ".*", ".+", ".*", ".*", rule_helpers.function_discard], + [".*", ".*<.*>.*", ".*", ".*", ".*", rule_helpers.function_discard], + # + # kshell.h, kconfigbase.sip have inline operators. + # + [".*", "operator\|", ".*", ".*", ".*", rule_helpers.function_discard], + # + # Inline operators. + # + ["KFileItem", "operator QVariant", ".*", ".*", ".*", rule_helpers.function_discard], + ["KService", "operator KPluginName", ".*", ".*", ".*", rule_helpers.function_discard], + ["KCalCore::Duration", "operator bool|operator!", ".*", ".*", "", rule_helpers.function_discard], + ["KPageDialog", "pageWidget|buttonBox", ".*", ".*", "", _function_discard_non_const], + [".*", ".*", ".*", ".*", ".*Private.*", _function_discard_protected], + # + # This fn does not exist. + # + [".*", "qt_check_for_QGADGET_macro", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP thinks there are duplicate signatures. + # + [".*", "qobject_cast", ".*", ".*", ".*", rule_helpers.function_discard], + [".*", "qobject_interface_iid", ".*", ".*", ".*", rule_helpers.function_discard], + # + # QDebug is not exposed. + # + [".*", "operator<<", ".*", "QDebug.*", ".*", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + # + # Annotate with Transfer or TransferThis when we see a parent object. + # + [".*", ".*", ".*", r"[KQ][A-Za-z_0-9]+\W*\*\W*parent", ".*", _parameter_transfer_to_parent], + ["KCoreConfigSkeleton", "addItem.*", "reference", ".*", ".*", rule_helpers.parameter_in], + ["KDateTime", "fromString", "negZero", ".*", ".*", rule_helpers.parameter_out], + ["KPty", "tcGetAttr|tcSetAttr", "ttmode", ".*", ".*", _parameter_rewrite_without_colons], + # + # TODO: Temporarily trim any parameters which start "enum". + # + ["KAboutData", ".*", "licenseType", ".*", ".*", _parameter_strip_class_enum], + ] + + +def typedef_rules(): + return [ + # + # Rewrite uid_t, gid_t as int. + # + [".*", ".*", ".*", "uid_t|gid_t", _typedef_rewrite_as_int], + # + # Rewrite without leading "::". + # + ["org::kde", "KDirNotify", "", ".*", _typedef_rewrite_without_colons], + ["org::kde", "KSSLDInterface", "", ".*", _typedef_rewrite_without_colons], + # + # There are two version of KSharedConfigPtr in ksharedconfig.h and kconfiggroup.h. + # + [".*", "KSharedConfigPtr", ".*", "QExplicitlySharedDataPointer", _typedef_discard], + # + # There are two version of Display in kstartupinfo.h and kxmessages.h. + # + ["kstartupinfo.h|kxmessages.h", "Display", ".*", ".*", _typedef_discard], + # + # Redundant typedef. + # + ["agenttype.h", "QVariantMap", ".*", ".*", _typedef_discard], + ] + + +def unexposed_rules(): + + return [ + ] + + +def variable_rules(): + + return [ + # + # Discard variable emitted by QOBJECT. + # + [".*", "staticMetaObject", ".*", rule_helpers.variable_discard], + # + # Discard "private" variables (check they are protected!). + # + [".*", "d_ptr", ".*", _variable_discard_protected], + [".*", "d", ".*Private.*", rule_helpers.variable_discard], + ] + + +class RuleSet(rules_engine.RuleSet): + """ + SIP file generator rules. This is a set of (short, non-public) functions + and regular expression-based matching rules. + """ + def __init__(self): + super(RuleSet, self).__init__() + # + # Add PyQt template support. + # + self.add_rules(rules_module=templates.PyQt) + # + # Add std-n-boost template support. + # + self.add_rules(rules_module=templates.std_n_boost) + # + # Add module-generic PyKF5 rules. + # + self.add_rules(rules_module=sys.modules[__name__], methodcode=common_methodcode.code, + modulecode=common_modulecode.code, typecode=common_typecode.code) + # + # Add module-specific PyKF5 rules. + # + for rules_module in [ + "Akonadi", + "Attica", + "Baloo", + "BluezQt", + "CalendarSupport", + "FollowupReminder", + "gpgme", + "IncidenceEditor", + "KActivitiesStats", + "Kaddressbook", + "KAlarmCal", + "KArchive", + "KAuth", + "KBlog", + "KBookmarks", + "KCalCore", + "KCalUtils", + "KCMUtils", + "KCoreAddons", + "KCodecs", + "KCompletion", + "KConfigCore", + "KConfigGui", + "KConfigWidgets", + "KContacts", + "KCrash", + "KDeclarative", + "KdepimDBusInterfaces", + "KDEWebKit", + "KDGantt2", + "KEmoticons", + "KExiv2", + "KF5KDEGames", + "KFace", + "KGAPI", + "KGeoMap", + "KGlobalAccel", + "KHtml", + "KIconThemes", + "KIdentityManagement", + "KIdleTime", + "KIMAP", + "KIO", + "KIOFileWidgets", + "KIOWidgets", + "KIPI", + "KItemViews", + "KI18n", + "KItemModels", + "KJobWidgets", + "KJS", + "KLDAP", + "KMbox", + "KMime", + "KNewStuff3", + "KNotifyConfig", + "KNotifications", + "KontactInterface", + "KPIMTextEdit", + "KPackage", + "KParts", + "KPlotting", + "KPty", + "Kross", + "KRunner", + "KScreen", + "KSieveUi", + "KService", + "KStyle", + "KTextEditor", + "KTextWidgets", + "KTNEF", + "KUnitConversion", + "KWidgetsAddons", + "KWindowSystem", + "KXmlGui", + "Libkdepim", + "Libkleo", + "MailCommon", + "MailImporter", + "MailTransport", + "MessageComposer", + "MessageCore", + "MessageList", + "MessageViewer", + "NetworkManagerQt", + "PimCommon", + "Plasma", + "SendLater", + "Solid", + "Sonnet", + "Syndication", + "TemplateParser", + "XsltKde", + ]: + self.add_rules( + rules_module=import_module("." + rules_module, self.__module__)) + self.pd_cache = None + + def _fill_cache(self): + if self.pd_cache is None: + self.pd_cache = rules_engine.get_platform_dependencies(os.path.dirname(os.path.realpath(__file__))) + + def _update_dir_set(self, result, key1, key2): + self._fill_cache() + for component, data in self.pd_cache[key1].items(): + dirlist = data[key2].split(";") + dirlist = [os.path.normpath(i) for i in dirlist if i] + result.update(dirlist) + + def cxx_source_root(self): + self._fill_cache() + return self.pd_cache["CXX_SOURCE_ROOT"] + + def cxx_sources(self): + source_root = self.cxx_source_root() + os.path.sep + result = set() + self._update_dir_set(result, "CXX_SOURCES", "INCLUDE_DIRS") + # + # We exclude anything which is not under the source root: those are dependencies! + # + result = sorted([i for i in result if i.startswith(source_root)]) + # + # Collapse any subdirectories. + # + i = 0 + j = 0 + c = "\x00" + while i < len(result): + if result[i].startswith(c + os.path.sep): + pass + else: + c = result[i] + result[j] = c + j += 1 + i += 1 + result = result[:j] + # + # Akonadi private. + # + result.append(os.path.join(source_root, "akonadi", "abstractsearchplugin.h")) + result.append(os.path.join(source_root, "akonadi", "private")) + result.append(os.path.join(source_root, "Attica", "attica", "(atticabasejob.h|platformdependent.h)")) + # + # Include KIOCore/kio/job_base.h. + # + result.append(os.path.join(source_root, "KIOCore", "kio", "job_base.h")) + # + # KF5KIO is missing .../kio. + # KF5JS is missing .../kjs and .../wtf. + # + result.append(os.path.join(source_root, "kio")) + result.append(os.path.join(source_root, "kjs")) + result.append(os.path.join(source_root, "wtf")) + result.append(os.path.join(source_root, "MailTransport", "mailtransport", "transportbase.h")) + result = sorted(result) + return result + + def cxx_includes(self): + source_root = self.cxx_source_root() + os.path.sep + result = set() + self._update_dir_set(result, "CXX_DEPENDENCIES", "INCLUDE_DIRS") + # + # We include anything which is not under the source root: those are dependencies too! + # + self._update_dir_set(result, "CXX_SOURCES", "INCLUDE_DIRS") + result = [i for i in result if not i.startswith(source_root)] + result = sorted(result) + return result + + def cxx_compile_flags(self): + result = set(self.pd_cache["CXX_COMPILE_OPTIONS"].split(";")) + self._update_dir_set(result, "CXX_DEPENDENCIES", "COMPILE_FLAGS") + self._update_dir_set(result, "CXX_DEPENDENCIES", "COMPILE_FLAGS") + result = [i for i in result] + result = sorted(result) + return result + + def cxx_libraries(self): + result = set() + self._update_dir_set(result, "CXX_SOURCES", "LIBRARIES") + self._update_dir_set(result, "CXX_DEPENDENCIES", "LIBRARIES") + result = [i for i in result] + result = sorted(result) + return result + + @property + def cxx_selector(self): + return re.compile(".*") + + @property + def cxx_omitter(self): + return re.compile("KDELibs4Support|ksslcertificatemanager_p.h") + + def sip_package(self): + self._fill_cache() + return self.pd_cache["SIP_PACKAGE"] + + def sip_imports(self): + self._fill_cache() + result = set() + dirlist = self.pd_cache["SIP_DEPENDENCIES"].split(";") + result.update(dirlist) + result = [i for i in result if i] + result = sorted(result) + return result Index: find-modules/module_generation/PyKF5/common_methodcode.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/common_methodcode.py @@ -0,0 +1,989 @@ +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Text snippets in this file are derived from PyKDE4, and are in turn derived +# from kde library code. The licence for the snippets is below. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as +# published by the Free Software Foundation; either version 2, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details +# +# You should have received a copy of the GNU Library General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +""" +SIP binding custom method-related code for PyKF5. +""" + + +# +# Main dictionary. +# +# For a top level object, when using sip_bulk_generator.py, it is important to use the name of any forwardee header in +# the key, since that is the file we actually use. +# +def code(): + return { +# ./kdecore/kurl.sip +"kdecore/kurl.h": +{ + "__len__": + { + "code": + """ + %MethodCode + //returns (int) + Py_BEGIN_ALLOW_THREADS + sipRes = sipCpp -> count(); + Py_END_ALLOW_THREADS + %End + """ + }, + "__setitem__": + { + "code": + """ + %MethodCode + //takes index | (int) | value | (KUrl) + int len; + + len = sipCpp -> count(); + + if ((a0 = sipConvertFromSequenceIndex(a0,len)) < 0) + sipIsErr = 1; + else + (*sipCpp)[a0] = *a1; + %End + """ + }, + "__setitem__": + { + "code": + """ + %MethodCode + //takes range | (a Python slice) | urlList | (KUrl.List) + SIP_SSIZE_T len, start, stop, step, slicelength, i; + + len = sipCpp -> count(); + + if (sipConvertFromSliceObject(a0,len,&start,&stop,&step,&slicelength) < 0) + sipIsErr = 1; + else + { + int vlen = a1 -> count(); + if (vlen != slicelength) + { + sipBadLengthForSlice(vlen,slicelength); + sipIsErr = 1; + } + else + { + KUrl::List::ConstIterator it = a1 -> begin(); + for (i = 0; i < slicelength; ++i) + { + (*sipCpp)[start] = *it; + start += step; + ++it; + } + } + } + %End + """ + }, + "__delitem__": + { + "code": + """ + %MethodCode + //takes index | (int) + int len; + + len = sipCpp -> count(); + + if ((a0 = sipConvertFromSequenceIndex(a0,len)) < 0) + sipIsErr = 1; + else + sipCpp -> removeAt(a0); + %End + """ + }, + "__delitem__": + { + "code": + """ + %MethodCode + //takes range | (a Python slice) + SIP_SSIZE_T len, start, stop, step, slicelength, i; + + len = sipCpp -> count(); + if (sipConvertFromSliceObject(a0,len,&start,&stop,&step,&slicelength) < 0) + sipIsErr = 1; + else + for (i = 0; i < slicelength; ++i) + { + sipCpp -> removeAt(start); + start += step - 1; + } + %End + """ + }, + "operator[]": + { + "parameters": "KUrl operator", + "cxx_parameters": "[] (int)", + "code": + """ + %MethodCode + //returns (KUrl) + //takes index | (int) + int len; + + len = sipCpp -> count(); + + if ((a0 = sipConvertFromSequenceIndex(a0,len)) < 0) + sipIsErr = 1; + else + sipRes = new KUrl((*sipCpp)[a0]); + %End + """ + }, + "operator[]": + { + "parameters": "KUrl::List operator", + "cxx_parameters": "[] (SIP_PYSLICE)", + "code": + """ + %MethodCode + //returns (KUrl.List) + //takes range | (a Python slice) + SIP_SSIZE_T len, start, stop, step, slicelength, i; + + len = sipCpp -> count(); + + if (sipConvertFromSliceObject(a0,len,&start,&stop,&step,&slicelength) < 0) + sipIsErr = 1; + else + { + sipRes = new KUrl::List(); + + for (i = 0; i < slicelength; ++i) + { + (*sipRes) += (*sipCpp)[start]; + start += step; + } + } + %End + """ + }, + "operator+": + { + "code": + """ + %MethodCode + //returns (KUrl.List) + //takes listToAdd | (KUrl.List) + Py_BEGIN_ALLOW_THREADS + // sipRes = new KUrl::List((const KUrl::List&)((*sipCpp) + *a0)); + sipRes = new KUrl::List (*sipCpp); + (*sipRes) += (*a0); + Py_END_ALLOW_THREADS + %End + """ + }, + "operator*": + { + "code": + """ + %MethodCode + sipRes = new KUrl::List(); + + for (int i = 0; i < a0; ++i) + (*sipRes) += (*sipCpp); + %End + """ + }, + "operator*=": + { + "code": + """ + %MethodCode + //returns (KUrl.List) + //takes val | (int) + KUrl::List orig(*sipCpp); + + sipCpp -> clear(); + + for (int i = 0; i < a0; ++i) + (*sipCpp) += orig; + %End + """ + }, + "__contains__": + { + "code": + """ + %MethodCode + //returns (bool) + //takes a0 | (KUrl) + // It looks like you can't assign QBool to int. + sipRes = bool(sipCpp->contains(*a0)); + %End + """ + }, +}, +# ./kdecore/kcmdlineargs.sip +"KCmdLineArgs::StdCmdLineArgs": +{ + "init": + { + "parameters": ["SIP_PYLIST argv", "const QByteArray& appname", "const QByteArray& catalog", "const KLocalizedString& programName", "const QByteArray& version", "const KLocalizedString& description = KLocalizedString()", "int stdargs = 3"], + "cxx_parameters": ["int", "char**", "const QByteArray&", "const QByteArray&", "const KLocalizedString&", "const QByteArray&", "const KLocalizedString& = KLocalizedString()", "KCmdLineArgs::StdCmdLineArgs = 3"], + "code": + """ + %MethodCode + KCmdLineArgs::StdCmdLineArgs cmdLineArgs = (KCmdLineArgs::StdCmdLineArgs) a6; + int argc, nargc; + char **argv; + + // Convert the list. + + if ((argv = pyArgvToC(a0, &argc)) == NULL) + return NULL; + + // Create it now the arguments are right. + nargc = argc; + + Py_BEGIN_ALLOW_THREADS + KCmdLineArgs::init (nargc, argv, *a1, *a2, *a3, *a4, *a5, cmdLineArgs); + Py_END_ALLOW_THREADS + + // Now modify the original list. + + updatePyArgv (a0, argc, argv); + %End + """ + }, + "init": + { + "parameters": ["SIP_PYLIST argv", "const KAboutData* about", "int stdargs = 3"], + "cxx_parameters": ["int", "char**", "const KAboutData*", "KCmdLineArgs::StdCmdLineArgs = 3"], + "code": + """ + %MethodCode + KCmdLineArgs::StdCmdLineArgs cmdLineArgs = (KCmdLineArgs::StdCmdLineArgs) a2; + int argc, nargc; + char **argv; + + // Convert the list. + + if ((argv = pyArgvToC(a0, &argc)) == NULL) + return NULL; + + // Create it now the arguments are right. + nargc = argc; + + Py_BEGIN_ALLOW_THREADS + KCmdLineArgs::init (nargc, argv, a1, cmdLineArgs); + Py_END_ALLOW_THREADS + + // Now modify the original list. + + updatePyArgv (a0, argc, argv); + %End + """ + }, +}, +# ./kdecore/typedefs.sip +"kdecore/typedefs.h": +{ + "version": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = KDE::version (); + Py_END_ALLOW_THREADS + %End + """ + }, + "versionMajor": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = KDE::versionMajor (); + Py_END_ALLOW_THREADS + %End + """ + }, + "versionMinor": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = KDE::versionMinor (); + Py_END_ALLOW_THREADS + %End + """ + }, + "versionRelease": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = KDE::versionRelease (); + Py_END_ALLOW_THREADS + %End + """ + }, + "versionString": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = KDE::versionString (); + Py_END_ALLOW_THREADS + %End + """ + }, + "pykde_version": + { + "code": + """ + %MethodCode + //version + sipRes = 0x040002; + %End + """ + }, + "pykde_versionMajor": + { + "code": + """ + %MethodCode + //major + sipRes = 0x04; + %End + """ + }, + "pykde_versionMinor": + { + "code": + """ + %MethodCode + //minor + sipRes = 0x00; + %End + """ + }, + "pykde_versionRelease": + { + "code": + """ + %MethodCode + //release + sipRes = 0x02; + %End + """ + }, + "pykde_versionString": + { + "code": + """ + %MethodCode + //string + sipRes = "4.0.2 Rev 2"; + %End + """ + }, +}, +# ./kio/kfileitem.sip +"KFileItem": +{ + "__len__": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = sipCpp -> count(); + Py_END_ALLOW_THREADS + %End + """ + }, + "__setitem__": + { + "code": + """ + %MethodCode + int len; + + len = sipCpp -> count(); + + if ((a0 = sipConvertFromSequenceIndex(a0,len)) < 0) + sipIsErr = 1; + else + (*sipCpp)[a0] = *(KFileItem *)a1; + %End + """ + }, + "__setitem__": + { + "code": + """ + %MethodCode + SIP_SSIZE_T len, start, stop, step, slicelength, i; + + len = sipCpp -> count(); + + if (sipConvertFromSliceObject(a0,len,&start,&stop,&step,&slicelength) < 0) + sipIsErr = 1; + else + { + int vlen = a1 -> count(); + if (vlen != slicelength) + { + sipBadLengthForSlice(vlen,slicelength); + sipIsErr = 1; + } + else + { + KFileItemList::ConstIterator it = a1 -> begin(); + for (i = 0; i < slicelength; ++i) + { + (*sipCpp)[start] = *it; + start += step; + ++it; + } + } + } + %End + """ + }, + "__delitem__": + { + "code": + """ + %MethodCode + int len; + + len = sipCpp -> count(); + + if ((a0 = sipConvertFromSequenceIndex(a0,len)) < 0) + sipIsErr = 1; + else + sipCpp -> removeAt ( a0); + %End + """ + }, + "__delitem__": + { + "code": + """ + %MethodCode + SIP_SSIZE_T len, start, stop, step, slicelength, i; + + len = sipCpp -> count(); + if (sipConvertFromSliceObject(a0,len,&start,&stop,&step,&slicelength) < 0) + sipIsErr = 1; + else + for (i = 0; i < slicelength; ++i) + { + sipCpp -> removeAt (start); + start += step - 1; + } + %End + """ + }, + "[]": + { + "parameters": "KFileItem operator", + "cxx_parameters": "[] (int)", + "code": + """ + %MethodCode + int len; + + len = sipCpp->count(); + + if ((a0 = (int)sipConvertFromSequenceIndex(a0, len)) < 0) + sipIsErr = 1; + else + sipRes = new KFileItem((*sipCpp)[a0]); + %End + """ + }, + "[]": + { + "parameters": "KFileItemList operator", + "cxx_parameters": "[] (SIP_PYSLICE)", + "code": + """ + %MethodCode + SIP_SSIZE_T len, start, stop, step, slicelength, i; + + len = sipCpp->count(); + + #if PY_VERSION_HEX >= 0x03020000 + if (PySlice_GetIndicesEx(a0, len, &start, &stop, &step, &slicelength) < 0) + #else + if (PySlice_GetIndicesEx((PySliceObject *)a0, len, &start, &stop, &step, &slicelength) < 0) + #endif + sipIsErr = 1; + else + { + sipRes = new KFileItemList(); + + for (i = 0; i < slicelength; ++i) + { + (*sipRes) += (*sipCpp)[start]; + start += step; + } + } + %End + """ + }, +}, +# ./solid/predicate.sip +"Predicate": +{ + "Predicate": + { + "parameters": "const Solid::DeviceInterface::Type ifaceType", + "cxx_parameters": "const Solid::DeviceInterface::Type&", + "cxx_fn_result": "void", + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipCpp = new Solid::Predicate (a0); + Py_END_ALLOW_THREADS + %End + """ + }, +}, +# ./ktexteditor/movingrange.sip +"KTextEditor::MovingRange": +{ + "start": + { + "code": + """ + %MethodCode + // Returning a ref of this class is problematic. + const KTextEditor::MovingCursor& cursor = sipCpp->start(); + sipRes = const_cast(&cursor); + %End + """ + }, + "end": + { + "code": + """ + %MethodCode + // Returning a ref of this class is problematic. + const KTextEditor::MovingCursor& cursor = sipCpp->end(); + sipRes = const_cast(&cursor); + %End + """ + }, +}, +# ./ktexteditor/view.sip +"./ktexteditor/view.h": +{ + "codeCompletionInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "sessionConfigInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "textHintInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "annotationViewInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "configInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "coordinatesToCursorInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "templateInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "templateInterface2": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, +}, +# ./ktexteditor/editor.sip +"KTextEditor/ktexteditor/editor.h": +{ + "commandInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "containerInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, +}, +# ./ktexteditor/document.sip +"KTextEditor::Document": +{ + "annotationInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "markInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "modificationInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "searchInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "variableInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "movingInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "highlightInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "configInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "parameterizedSessionConfigInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "sessionConfigInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, + "recoveryInterface": + { + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipRes = dynamic_cast(sipCpp); + Py_END_ALLOW_THREADS + %End + """ + }, +}, +# ./kdeui/kapplication.sip +"/kdeui/kapplication.h": +{ + "KApplication": + { + "parameters": ["Display* display", "SIP_PYLIST list", "const QByteArray& rAppName", "bool GUIenabled = 1"], + "cxx_parameters": ["Display*", "int&", "char**", "const QByteArray&", "bool = 1"], + "cxx_fn_result": "void", + "code": + """ + %MethodCode + // The Python interface is a list of argument strings that is modified. + + int argc; + char **argv; + + // Convert the list. + if ((argv = kdeui_ArgvToC(a1, argc)) == NULL) + sipIsErr = 1; + else + { + // Create it now the arguments are right. + static int nargc; + nargc = argc; + + Py_BEGIN_ALLOW_THREADS + sipCpp = new sipKApplication(a0, nargc, argv, *a2, a3); + Py_END_ALLOW_THREADS + + // Now modify the original list. + kdeui_UpdatePyArgv(a1, argc, argv); + } + %End + """ + }, +}, +# ./kdeui/kfontdialog.sip +"KFontDialog": +{ + "KFontDialog": + { + "parameters": ["QWidget* parent /TransferThis/ = 0", "const KFontChooser::DisplayFlags& flags = KFontChooser::NoDisplayFlags", "const QStringList& fontlist = QStringList()", "Qt::CheckState* sizeIsRelativeState = 0"], + "cxx_parameters": ["QWidget* = 0", "const KFontChooser::DisplayFlags& = KFontChooser::NoDisplayFlags", "const QStringList& = QStringList()", "Qt::CheckState* = 0"], + "cxx_fn_result": "void", + "code": + """ + %MethodCode + Py_BEGIN_ALLOW_THREADS + sipCpp= new sipKFontDialog (a0, *a1, *a2, &a3); + Py_END_ALLOW_THREADS + %End + """ + }, + "getFont": + { + "parameters": ["QFont& theFont", "const KFontChooser::DisplayFlags& flags = KFontChooser::NoDisplayFlags", "QWidget* parent /Transfer/ = 0", "Qt::CheckState* sizeIsRelativeState = Qt::Unchecked"], + "fn_result": "SIP_PYTUPLE", + "cxx_parameters": ["QFont&", "const KFontChooser::DisplayFlags& = KFontChooser::NoDisplayFlags", "QWidget* = 0", "Qt::CheckState* = 0"], + "cxx_fn_result": "int", + "code": + """ + %MethodCode + int result; + Py_BEGIN_ALLOW_THREADS + result = KFontDialog::getFont (*a0, *a1, a2, &a3); + Py_END_ALLOW_THREADS + #if PY_MAJOR_VERSION >= 3 + sipRes = PyLong_FromLong (result); + #else + sipRes = PyInt_FromLong (result); + #endif + %End + """ + }, + "getFontDiff": + { + "parameters": ["QFont& theFont", "KFontChooser::FontDiffFlags& diffFlags", "const KFontChooser::DisplayFlags& flags = KFontChooser::NoDisplayFlags", "QWidget* parent /Transfer/ = 0", "Qt::CheckState sizeIsRelativeState = Qt::Unchecked"], + "fn_result": "SIP_PYTUPLE", + "cxx_parameters": ["QFont&", "KFontChooser::FontDiffFlags&", "const KFontChooser::DisplayFlags& = KFontChooser::NoDisplayFlags", "QWidget* = 0", "Qt::CheckState* = 0"], + "cxx_fn_result": "int", + "code": + """ + %MethodCode + int result; + Py_BEGIN_ALLOW_THREADS + result = KFontDialog::getFontDiff (*a0, *a1, *a2, a3, &a4); + Py_END_ALLOW_THREADS + + #if PY_MAJOR_VERSION >= 3 + sipRes = PyLong_FromLong (result); + #else + sipRes = PyInt_FromLong (result); + #endif + %End + """ + }, + "getFontAndText": + { + "parameters": ["QFont& theFont", "QString& theString", "const KFontChooser::DisplayFlags& flags = KFontChooser::NoDisplayFlags", "QWidget* parent /Transfer/ = 0", "Qt::CheckState sizeIsRelativeState = Qt::Unchecked"], + "fn_result": "SIP_PYTUPLE", + "cxx_parameters": ["QFont&", "QString&", "const KFontChooser::DisplayFlags& = KFontChooser::NoDisplayFlags", "QWidget* = 0", "Qt::CheckState* = 0"], + "cxx_fn_result": "int", + "code": + """ + %MethodCode + int result; + Py_BEGIN_ALLOW_THREADS + result = KFontDialog::getFontAndText (*a0, *a1, *a2, a3, &a4); + Py_END_ALLOW_THREADS + + #if PY_MAJOR_VERSION >= 3 + sipRes = PyLong_FromLong (result); + #else + sipRes = PyInt_FromLong (result); + #endif + %End + """ + }, +}, +} Index: find-modules/module_generation/PyKF5/common_modulecode.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/common_modulecode.py @@ -0,0 +1,41 @@ +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +""" +SIP binding custom module-related code for PyKF5. +""" + + +# +# Main dictionary. +# +# When using sip_bulk_generator.py, it is important to use the name of any forwardee header in +# the key, since that is the file we actually use. +# +def code(): + return { + } Index: find-modules/module_generation/PyKF5/common_typecode.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/common_typecode.py @@ -0,0 +1,2268 @@ +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +# Text snippets in this file are derived from PyKDE4, and are in turn derived +# from kde library code. The licence for the snippets is below. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library General Public License as +# published by the Free Software Foundation; either version 2, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details +# +# You should have received a copy of the GNU Library General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +""" +SIP binding custom type-related code for PyKF5. +""" + +from utils import HeldAs +from templates.PyQt import list_typecode + + +_kdeui_qobject_ctscc = """ +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KActionCategory; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KActionCollection; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KCategoryDrawerV2; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCategoryDrawerV3; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCompletion; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KConfigDialogManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KConfigSkeleton; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KFind; + if (dynamic_cast(sipCpp)) + sipType = sipType_KReplace; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KGlobalAccel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KGlobalSettings; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KGlobalShortcutInfo; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KHelpMenu; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIconLoader; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KAbstractWidgetJobTracker; + if (dynamic_cast(sipCpp)) + sipType = sipType_KStatusBarJobTracker; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KWidgetJobTracker; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUiServerJobTracker; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDialogJobUiDelegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMessageBoxMessageHandler; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KModelIndexProxyMapper; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KModifierKeyInfo; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNotification; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNotificationRestrictions; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPageWidgetItem; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPassivePopupMessageHandler; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPixmapSequenceOverlayPainter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSelectionOwner; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSelectionWatcher; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KStartupInfo; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KStatusNotifierItem; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KViewStateMaintainerBase; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KViewStateSaver; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KWallet_Wallet; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KXMLGUIFactory; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KWidgetItemDelegate; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KExtendableItemDelegate; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KPageModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_KPageWidgetModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDescendantsProxyModel; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KIdentityProxyModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCheckableProxyModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSelectionProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCategorizedSortFilterProxyModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KRecursiveFilterProxyModel; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KAction; + if (dynamic_cast(sipCpp)) + sipType = sipType_KActionMenu; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDualAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPasteTextAction; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KSelectAction; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCodecAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFontAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFontSizeAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KRecentFilesAction; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KToggleAction; + if (dynamic_cast(sipCpp)) + sipType = sipType_KToggleFullScreenAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KToggleToolBarAction; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KToolBarLabelAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KToolBarPopupAction; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KToolBarSpacerAction; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KApplication; + if (dynamic_cast(sipCpp)) + sipType = sipType_KUniqueApplication; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBreadcrumbSelectionModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLinkItemSelectionModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KStyle; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSvgRenderer; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Sonnet_Highlighter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSystemTrayIcon; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUndoStack; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDateValidator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFloatValidator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIntValidator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMimeTypeValidator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KStringListValidator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDoubleValidator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KActionSelector; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCModule; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCapacityBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCharSelect; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDateTable; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDateTimeEdit; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDateTimeWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDateWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KEditListWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFadeWidgetEffect; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFilterProxySearchLine; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFontChooser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFontRequester; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KKeySequenceWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLanguageButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KLed; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMultiTabBar; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KNumInput; + if (dynamic_cast(sipCpp)) + sipType = sipType_KDoubleNumInput; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIntNumInput; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KPageView; + if (dynamic_cast(sipCpp)) + sipType = sipType_KPageWidget; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPixmapRegionSelectorWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPixmapSequenceWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KShortcutWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KShortcutsEditor; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTitleWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTreeWidgetSearchLineWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KXMessages; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KXYSelector; + if (dynamic_cast(sipCpp)) + sipType = sipType_KHueSaturationSelector; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KArrowButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KColorButton; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KMultiTabBarButton; + if (dynamic_cast(sipCpp)) + sipType = sipType_KMultiTabBarTab; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPushButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KAnimatedButton; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KRuler; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KSelector; + if (dynamic_cast(sipCpp)) + sipType = sipType_KColorValueSelector; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KGradientSelector; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KIntSpinBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KColorCombo; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KComboBox; + if (dynamic_cast(sipCpp)) + sipType = sipType_KDateComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFontComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KHistoryComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTimeComboBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Sonnet_DictionaryComboBox; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KDialog; + if (dynamic_cast(sipCpp)) + sipType = sipType_KAboutApplicationDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KBugReport; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KColorDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KEditToolBar; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KFindDialog; + if (dynamic_cast(sipCpp)) + sipType = sipType_KReplaceDialog; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KFontDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KNewPasswordDialog; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KPageDialog; + if (dynamic_cast(sipCpp)) + sipType = sipType_KAssistantDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KConfigDialog; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPasswordDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPixmapRegionSelectorDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KProgressDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KShortcutsDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTipDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Sonnet_ConfigDialog; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Sonnet_Dialog; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDialogButtonBox; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KColorPatch; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KDatePicker; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KHBox; + if (dynamic_cast(sipCpp)) + sipType = sipType_KVBox; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMessageWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPassivePopup; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPlotWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KPopupFrame; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KRatingWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSeparator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KCategorizedView; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KListWidget; + if (dynamic_cast(sipCpp)) + sipType = sipType_KCompletionBox; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KColorCells; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTimeZoneWidget; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KTextEdit; + if (dynamic_cast(sipCpp)) + { + sipType = sipType_KRichTextEdit; + if (dynamic_cast(sipCpp)) + sipType = sipType_KRichTextWidget; + } + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTextBrowser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSqueezedTextLabel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KUrlLabel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KButtonGroup; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KEditListBox; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KLineEdit; + if (dynamic_cast(sipCpp)) + sipType = sipType_KListWidgetSearchLine; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KRestrictedLine; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTreeWidgetSearchLine; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KMainWindow; + if (dynamic_cast(sipCpp)) + sipType = sipType_KXmlGuiWindow; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMenu; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KMenuBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSplashScreen; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KStatusBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTabBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTabWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KToolBar; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Sonnet_ConfigWidget; +%End +""" + + +# +# Main dictionary. +# +# For a top level object, when using sip_bulk_generator.py, it is important to use the name of any forwardee header in +# the key, since that is the file we actually use. +# +def code(): + return { +# ./khtml/dom_misc.sip +"DOM::DomShared": { #DomShared +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'DomShared' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_CustomNodeFilter; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_EventListener; +%End +""" +}, +# ./khtml/khtml_part.sip +"khtml_part.h::KHTMLPart": { #KHTMLPart : KParts::ReadOnlyPart +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KHTMLPart; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KHTMLView; +%End +""" +}, +# ./khtml/dom_element.sip +"DOM::Attr": { #Attr : DOM::Node +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Node' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_Attr; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_DOM_CharacterData; + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_Comment; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_DOM_Text; + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_CDATASection; + } + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_DOM_Document; + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLDocument; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_DocumentFragment; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_DocumentType; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_DOM_Element; + if (dynamic_cast(sipCpp)) + { + sipType = sipType_DOM_HTMLElement; + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLAnchorElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLAppletElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLAreaElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLBRElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLBaseElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLBaseFontElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLBlockquoteElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLBodyElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLButtonElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLDListElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLDirectoryElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLDivElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLFieldSetElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLFontElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLFormElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLFrameElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLFrameSetElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLHRElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLHeadElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLHeadingElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLHtmlElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLIFrameElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLImageElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLInputElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLIsIndexElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLLIElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLLabelElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLLayerElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLLegendElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLLinkElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLMapElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLMenuElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLMetaElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLModElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLOListElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLObjectElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLOptGroupElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLOptionElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLParagraphElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLParamElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLPreElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLQuoteElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLScriptElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLSelectElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLStyleElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTableCaptionElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTableCellElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTableColElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTableElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTableRowElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTableSectionElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTextAreaElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLTitleElement; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_HTMLUListElement; + } + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_Entity; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_EntityReference; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_Notation; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_ProcessingInstruction; +%End +""" +}, +# ./khtml/dom2_events.sip +"DOM::MutationEvent": { #MutationEvent : DOM::Event +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Event' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_MutationEvent; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_DOM_UIEvent; + if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_KeyboardEvent; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_MouseEvent; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DOM_TextEvent; + } +%End +""" +}, +# ./kparts/event.sip +"KParts::Event": { #Event : QEvent +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QEvent' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + { + sipType = sipType_KParts_Event; + if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_GUIActivateEvent; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_OpenUrlEvent; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_PartActivateEvent; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_PartSelectEvent; + } +%End +""" +}, +# ./kparts/browserextension.sip +"KParts::BrowserExtension": { #BrowserExtension : QObject +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_BrowserExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_BrowserHostExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_BrowserInterface; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_FileInfoExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_HistoryProvider; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_HtmlExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_LiveConnectExtension; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_KParts_Part; + if (dynamic_cast(sipCpp)) + { + sipType = sipType_KParts_ReadOnlyPart; + if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_ReadWritePart; + } + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_PartManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_Plugin; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_ScriptableExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_StatusBarExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_TextExtension; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_BrowserRun; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_MainWindow; +%End +""" +}, +# ./kparts/factory.sip +"Factory": { #Factory : KLibFactory +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Factory' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KParts_Factory; +%End +""" +}, +# ./phonon/medianode.sip +"MediaNode": { #MediaNode /NoDefaultCtors/ +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'MediaNode' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + { + sipType = sipType_Phonon_AbstractAudioOutput; + if (dynamic_cast(sipCpp)) + sipType = sipType_Phonon_AudioDataOutput; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Phonon_AbstractVideoOutput; + if (dynamic_cast(sipCpp)) + sipType = sipType_Phonon_VideoWidget; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Phonon_AudioOutput; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Phonon_Effect; + if (dynamic_cast(sipCpp)) + sipType = sipType_Phonon_VolumeFaderEffect; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Phonon_MediaObject; +%End +""" +}, +# ./phonon/abstractvideodataoutput.sip +"QSet": { #QSet +"code": +""" +%TypeHeaderCode +#include +#include +%End +%ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + QSet set = *sipCpp; + int i = 0; + foreach (Phonon::Experimental::VideoFrame2::Format value, set) + { + PyObject *obj = PyInt_FromLong ((long) value); + if (obj == NULL || PyList_SET_ITEM (l, i, obj) < 0) + { + Py_DECREF(l); + + if (obj) + Py_DECREF(obj); + + return NULL; + } + + Py_DECREF(obj); + i++; + } + + return l; +%End +%ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + QSet *qs = new QSet; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + Phonon::Experimental::VideoFrame2::Format t = (Phonon::Experimental::VideoFrame2::Format)PyInt_AS_LONG (PyList_GET_ITEM (sipPy, i)); + *qs << t; + + } + + *sipCppPtr = qs; + + return sipGetState(sipTransferObj); +%End +""" +}, +# ./phonon/pulsesupport.sip +"PulseSupport": { #PulseSupport : QObject +"code": +""" +%TypeHeaderCode +#include +#include +%End +""" +}, +# ./phonon/mrl.sip +"Mrl": { #Mrl : QUrl +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QUrl' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Phonon_Mrl; +%End +""" +}, +# ./phonon/abstractaudiodataoutput.sip +"QSet": { #QSet +"code": +""" +%TypeHeaderCode +#include +#include +%End +%ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + QSet set = *sipCpp; + int i = 0; + foreach (Phonon::Experimental::AudioFormat value, set) + { + PyObject *obj = PyInt_FromLong ((long) value); + if (obj == NULL || PyList_SET_ITEM (l, i, obj) < 0) + { + Py_DECREF(l); + + if (obj) + Py_DECREF(obj); + + return NULL; + } + + Py_DECREF(obj); + i++; + } + + return l; +%End +%ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + } + + QSet *qs = new QSet; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + Phonon::Experimental::AudioFormat t = (Phonon::Experimental::AudioFormat)PyInt_AS_LONG (PyList_GET_ITEM (sipPy, i)); + *qs << t; + + } + + *sipCppPtr = qs; + + return sipGetState(sipTransferObj); +%End +""" +}, +# ./phonon/videowidget.sip +"VideoWidget": { #VideoWidget : QWidget, Phonon::AbstractVideoOutput +"code": +""" +%TypeHeaderCode +#include +#include +%End +""" +}, +# ./phonon/objectdescription.sip +"QList": { #QList +"code": +""" +%ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + for (int i = 0; i < sipCpp->size(); ++i) + { + DNSSD::RemoteService::Ptr *t = new Phonon::ObjectDescription (sipCpp->at(i)); + PyObject *tobj; + + if ((tobj = sipConvertFromNewInstance(t->data(), sipClass_DNSSD_RemoteService, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; + + return NULL; + } + + PyList_SET_ITEM(l, i, tobj); + } + + return l; +%End +%ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_DNSSD_RemoteService, SIP_NOT_NONE)) + return 0; + + return 1; + } + + QList *ql = new QList; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; + DNSSD::RemoteService *t = reinterpret_cast(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_DNSSD_RemoteService, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { + sipReleaseInstance(t, sipClass_DNSSD_RemoteService, state); + + delete ql; + return 0; + } + + DNSSD::RemoteService::Ptr *tptr = new DNSSD::RemoteService::Ptr (t); + + ql->append(*tptr); + + sipReleaseInstance(t, sipClass_DNSSD_RemoteService, state); + } + + *sipCppPtr = ql; + + return sipGetState(sipTransferObj); +%End +""" +}, +# ./nepomuk/ktagcloudwidget.sip +"KTagCloudWidget": { #KTagCloudWidget : QWidget +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QWidget' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + { + sipType = sipType_KTagCloudWidget; + if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_TagCloud; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTagDisplayWidget; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_TagWidget; +%End +""" +}, +# ./nepomuk/entity.sip +"Entity": { #Entity +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Entity' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_Types_Class; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_Types_Ontology; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_Types_Property; +%End +""" +}, +# ./nepomuk/resource.sip +"Resource": { #Resource +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Resource' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_File; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_Tag; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Nepomuk_Thing; +%End +""" +}, +# ./soprano/queryresultiterator.sip +"QueryResultIterator": { #QueryResultIterator +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QueryResultIterator' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_DBusQueryResultIterator; +%End +""" +}, +# ./soprano/statementiterator.sip +"StatementIterator": { #StatementIterator +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'StatementIterator' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_DBusStatementIterator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_SimpleStatementIterator; +%End +""" +}, +# ./soprano/plugin.sip +"Plugin": { #Plugin +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Plugin' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Backend; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Parser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Serializer; +%End +""" +}, +# ./soprano/error.sip +"Error": { #Error +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'Error' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Error_ParserError; +%End +""" +}, +# ./soprano/pluginmanager.sip +"QList": { #QList + "code": list_typecode, + "value": { + "type": "Soprano::Backend", + "HeldAs": HeldAs.POINTER, + }, +}, +"QList": { #QList + "code": list_typecode, + "value": { + "type": "Soprano::Parser", + "HeldAs": HeldAs.POINTER, + }, +}, +"QList": { #QList + "code": list_typecode, + "value": { + "type": "Soprano::Serializer", + "HeldAs": HeldAs.POINTER, + }, +}, +# ./soprano/model.sip +"Model": { #Model : QObject, Soprano::Error::ErrorCache +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_DBusClient; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_LocalSocketClient; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_TcpClient; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Soprano_Model; + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_SparqlModel; + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Soprano_FilterModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Inference_InferenceModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_NRLModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Server_DBusExportModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_AsyncModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_MutexModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_SignalCacheModel; + } + else if (dynamic_cast(sipCpp)) + { + sipType = sipType_Soprano_StorageModel; + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_DBusModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_DummyModel; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_PluginManager; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Server_DBusExportIterator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Server_ServerCore; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_AsyncQuery; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_AsyncResult; +%End +""" +}, +# ./soprano/tcpclient.sip +"TcpClient": { #TcpClient : QObject, Soprano::Error::ErrorCache +"code": +""" +%TypeHeaderCode +#include +#include +%End +""" +}, +# ./soprano/nodeiterator.sip +"NodeIterator": { #NodeIterator +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'NodeIterator' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Client_DBusNodeIterator; + else if (dynamic_cast(sipCpp)) + sipType = sipType_Soprano_Util_SimpleNodeIterator; +%End +""" +}, +# ./kdecore/kmimetype.sip +"QList": { #QList + "code": list_typecode, + "value": { + "type": "KMimeType", + "ptr": "KMimeType::Ptr", + "HeldAs": HeldAs.POINTER, + }, +}, +# ./kdecore/kurl.sip +"KUrl": { #KUrl : QUrl +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QUrl' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KUrl; +%End +""" +}, +# ./kdecore/kcmdlineargs.sip +"KCmdLineOptions": { #KCmdLineOptions +"code": +""" +%TypeHeaderCode +#include +extern char **pyArgvToC(PyObject *argvlist,int *argcp); +extern void updatePyArgv(PyObject *argvlist,int argc,char **argv); +%End +""" +}, +"KCmdLineArgs": { #KCmdLineArgs +"code": +""" +%TypeHeaderCode +#include +#include +extern char **pyArgvToC(PyObject *argvlist,int *argcp); +extern void updatePyArgv(PyObject *argvlist,int argc,char **argv); +%End +""" +}, +# ./kdecore/kconfig.sip +"kconfig.h::KConfig": { #KConfig : KConfigBase +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KConfigBase' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + { + sipType = sipType_KConfig; + if (dynamic_cast(sipCpp)) + sipType = sipType_KDesktopFile; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KSharedConfig; + } + else if (dynamic_cast(sipCpp)) + sipType = sipType_KConfigGroup; +%End +""" +}, +"KSharedPtr": { #KSharedPtr +"code": +""" +%ConvertFromTypeCode + // Convert to a Python instance + + if (!sipCpp) + return NULL; + + KSharedPtr *cPtr = new KSharedPtr (*(KSharedPtr *)sipCpp); + TYPE *cpp = cPtr->data (); + PyObject *obj = sipConvertFromType(cpp, sipType_TYPE, sipTransferObj); + + return obj; +%End +%ConvertToTypeCode + // Convert a Python instance to a Ptr on the heap. + if (sipIsErr == NULL) { + return 1; + } + + int iserr = 0; + TYPE *cpp = (TYPE *)sipForceConvertToType(sipPy, sipType_TYPE, NULL, 0, NULL, &iserr); + + if (iserr) + { + *sipIsErr = 1; + return 0; + } + + *sipCppPtr = new KSharedPtr (cpp); + + return 1; +%End +""" +}, +"QHash": { #QHash +"code": +""" +%ConvertFromTypeCode + // Create the dictionary. + PyObject *d = PyDict_New(); + + if (!d) + return NULL; + + // Set the dictionary elements. + QHash::const_iterator i = sipCpp->constBegin(); + + while (i != sipCpp->constEnd()) + { + TYPE1 *t1 = new TYPE1(i.key()); + TYPE2 *t2 = new TYPE2(i.value()); + + PyObject *t1obj = sipConvertFromNewInstance(t1, sipClass_TYPE1, sipTransferObj); + PyObject *t2obj = sipConvertFromNewInstance(t2, sipClass_TYPE2, sipTransferObj); + + if (t1obj == NULL || t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0) + { + Py_DECREF(d); + + if (t1obj) + Py_DECREF(t1obj); + else + delete t1; + + if (t2obj) + Py_DECREF(t2obj); + else + delete t2; + + return NULL; + } + + Py_DECREF(t1obj); + Py_DECREF(t2obj); + + ++i; + } + + return d; +%End +%ConvertToTypeCode + PyObject *t1obj, *t2obj; + SIP_SSIZE_T i = 0; + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyDict_Check(sipPy)) + return 0; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + if (!sipCanConvertToInstance(t1obj, sipClass_TYPE1, SIP_NOT_NONE)) + return 0; + + if (!sipCanConvertToInstance(t2obj, sipClass_TYPE2, SIP_NOT_NONE)) + return 0; + } + + return 1; + } + + QHash *qm = new QHash; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + int state1, state2; + + TYPE1 *t1 = reinterpret_cast(sipConvertToInstance(t1obj, sipClass_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr)); + TYPE2 *t2 = reinterpret_cast(sipConvertToInstance(t2obj, sipClass_TYPE2, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); + + if (*sipIsErr) + { + sipReleaseInstance(t1, sipClass_TYPE1, state1); + sipReleaseInstance(t2, sipClass_TYPE2, state2); + + delete qm; + return 0; + } + + qm->insert(*t1, *t2); + + sipReleaseInstance(t1, sipClass_TYPE1, state1); + sipReleaseInstance(t2, sipClass_TYPE2, state2); + } + + *sipCppPtr = qm; + + return sipGetState(sipTransferObj); +%End +""" +}, +"QHash": { #QHash +"code": +""" +%ConvertFromTypeCode + // Create the dictionary. + PyObject *d = PyDict_New(); + + if (!d) + return NULL; + + // Set the dictionary elements. + QHash::const_iterator i = sipCpp->constBegin(); + + while (i != sipCpp->constEnd()) + { + TYPE1 *t1 = new TYPE1(i.key()); + TYPE2 *t2 = i.value(); + + PyObject *t1obj = sipConvertFromNewType(t1, sipType_TYPE1, sipTransferObj); + PyObject *t2obj = sipConvertFromNewType(t2, sipType_TYPE2, sipTransferObj); + + if (t1obj == NULL || t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0) + { + Py_DECREF(d); + + if (t1obj) + Py_DECREF(t1obj); + else + delete t1; + + if (t2obj) + Py_DECREF(t2obj); + else + delete t2; + + return NULL; + } + + Py_DECREF(t1obj); + Py_DECREF(t2obj); + + ++i; + } + + return d; +%End +%ConvertToTypeCode + PyObject *t1obj, *t2obj; + SIP_SSIZE_T i = 0; + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyDict_Check(sipPy)) + return 0; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + if (!sipCanConvertToType(t1obj, sipType_TYPE1, SIP_NOT_NONE)) + return 0; + + if (!sipCanConvertToType(t2obj, sipType_TYPE2, SIP_NOT_NONE)) + return 0; + } + + return 1; + } + + QHash *qm = new QHash; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + int state1, state2; + + TYPE1 *t1 = reinterpret_cast(sipConvertToType(t1obj, sipType_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr)); + TYPE2 *t2 = reinterpret_cast(sipConvertToType(t2obj, sipType_TYPE2, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); + + if (*sipIsErr) + { + sipReleaseType(t1, sipType_TYPE1, state1); + sipReleaseType(t2, sipType_TYPE2, state2); + + delete qm; + return 0; + } + + qm->insert(*t1, t2); + + sipReleaseType(t1, sipType_TYPE1, state1); + sipReleaseType(t2, sipType_TYPE2, state2); + } + + *sipCppPtr = qm; + + return sipGetState(sipTransferObj); +%End +""" +}, +"QPair": { #QPair +"code": +""" +%ConvertFromTypeCode + // Create the tuple. + TYPE1 *t1 = new TYPE1(sipCpp->first); + TYPE2 *t2 = new TYPE2(sipCpp->second); + + PyObject *t1obj = sipConvertFromNewType(t1, sipType_TYPE1, sipTransferObj); + PyObject *t2obj = sipConvertFromNewType(t2, sipType_TYPE2, sipTransferObj); + + if (t1obj == NULL || t2obj == NULL) + { + if (t1obj) + Py_DECREF(t1obj); + else + delete t1; + + if (t2obj) + Py_DECREF(t2obj); + else + delete t2; + + return NULL; + } + + return Py_BuildValue((char *)"NN", t1obj, t2obj); +%End +%ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + return (PyTuple_Size(sipPy) == 2); + + + int state1, state2; + + PyObject *t1obj = PyTuple_GET_ITEM(sipPy, 0); + PyObject *t2obj = PyTuple_GET_ITEM(sipPy, 1); + + TYPE1 *t1 = reinterpret_cast(sipConvertToType(t1obj, sipType_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr)); + TYPE2 *t2 = reinterpret_cast(sipConvertToType(t2obj, sipType_TYPE2, sipTransferObj, SIP_NOT_NONE, &state2, sipIsErr)); + + if (*sipIsErr) + { + sipReleaseType(t1, sipType_TYPE1, state1); + sipReleaseType(t2, sipType_TYPE2, state2); + + return 0; + } + + QPair *qp = new QPair; + + qp->first = *t1; + qp->second = *t2; + + *sipCppPtr = qp; + + return sipGetState(sipTransferObj); +%End +""" +}, +"QStack": { #QStack +"code": +""" +%ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + for (int i = 0; i < sipCpp->size(); ++i) + { + TYPE *t = (TYPE *)(sipCpp->at(i)); + PyObject *tobj; + + if ((tobj = sipConvertFromNewInstance(t, sipClass_TYPE, sipTransferObj)) == NULL) + { + Py_DECREF(l); + delete t; + + return NULL; + } + + PyList_SET_ITEM(l, i, tobj); + } + + return l; +%End +%ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + if (!sipCanConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_TYPE, SIP_NOT_NONE)) + return 0; + + return 1; + } + + QStack *qv = new QStack; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + int state; + TYPE *t = reinterpret_cast(sipConvertToInstance(PyList_GET_ITEM(sipPy, i), sipClass_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + + if (*sipIsErr) + { + sipReleaseInstance(t, sipClass_TYPE, state); + + delete qv; + return 0; + } + + qv->append(t); + + sipReleaseInstance(t, sipClass_TYPE, state); + } + + *sipCppPtr = qv; + + return sipGetState(sipTransferObj); +%End +""" +}, +"QHash": { #QHash +"code": +""" +%ConvertFromTypeCode + // Create the dictionary. + PyObject *d = PyDict_New(); + + if (!d) + return NULL; + + // Set the dictionary elements. + QHash::const_iterator i = sipCpp->constBegin(); + + while (i != sipCpp->constEnd()) + { + int t1 = i.key(); + int t2 = i.value(); + +#if PY_MAJOR_VERSION >= 3 + PyObject *t1obj = PyLong_FromLong ((long)t1); + PyObject *t2obj = PyLong_FromLong ((long)t2); +#else + PyObject *t1obj = PyInt_FromLong ((long)t1); + PyObject *t2obj = PyInt_FromLong ((long)t2); +#endif + + if (PyDict_SetItem(d, t1obj, t2obj) < 0) + { + Py_DECREF(d); + + if (t1obj) + Py_DECREF(t1obj); + + if (t2obj) + Py_DECREF(t2obj); + + return NULL; + } + + Py_DECREF(t1obj); + Py_DECREF(t2obj); + + ++i; + } + + return d; +%End +%ConvertToTypeCode + PyObject *t1obj, *t2obj; + SIP_SSIZE_T i = 0; + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyDict_Check(sipPy)) + return 0; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { +#if PY_MAJOR_VERSION >= 3 + if (!PyNumber_Check (t1obj)) +#else + if (!PyInt_Check (t1obj)) +#endif + return 0; + +#if PY_MAJOR_VERSION >= 3 + if (!PyNumber_Check (t2obj)) +#else + if (!PyInt_Check (t2obj)) +#endif + return 0; + } + + return 1; + } + + QHash *qm = new QHash; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + int state2; + +#if PY_MAJOR_VERSION >= 3 + int t1 = PyLong_AsLong (t1obj); +#else + int t1 = PyInt_AS_LONG (t1obj); +#endif + +#if PY_MAJOR_VERSION >= 3 + int t2 = PyLong_AsLong (t2obj); +#else + int t2 = PyInt_AS_LONG (t2obj); +#endif + + if (*sipIsErr) + { + delete qm; + return 0; + } + + qm->insert(t1, t2); + } + + *sipCppPtr = qm; + + return sipGetState(sipTransferObj); +%End +""" +}, +"QHash": { #QHash +"code": +""" +%ConvertFromTypeCode + // Create the dictionary. + PyObject *d = PyDict_New(); + + if (!d) + return NULL; + + // Set the dictionary elements. + QHash::const_iterator i = sipCpp->constBegin(); + + while (i != sipCpp->constEnd()) + { + TYPE1 *t1 = new TYPE1(i.key()); + bool t2 = i.value(); + + PyObject *t1obj = sipConvertFromNewType(t1, sipType_TYPE1, sipTransferObj); +#if PY_MAJOR_VERSION >= 3 + PyObject *t2obj = PyBool_FromLong ((long)t2); +#else + PyObject *t2obj = PyBool_FromLong ((long)t2); +#endif + + if (t1obj == NULL || t2obj == NULL || PyDict_SetItem(d, t1obj, t2obj) < 0) + { + Py_DECREF(d); + + if (t1obj) { + Py_DECREF(t1obj); + } else { + delete t1; + } + + if (t2obj) { + Py_DECREF(t2obj); + } + + return NULL; + } + + Py_DECREF(t1obj); + Py_DECREF(t2obj); + + ++i; + } + + return d; +%End +%ConvertToTypeCode + PyObject *t1obj, *t2obj; + SIP_SSIZE_T i = 0; + + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyDict_Check(sipPy)) + return 0; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + if (!sipCanConvertToType(t1obj, sipType_TYPE1, SIP_NOT_NONE)) + return 0; + +#if PY_MAJOR_VERSION >= 3 + if (!PyBool_Check (t2obj)) +#else + if (!PyBool_Check (t2obj)) +#endif + return 0; + } + + return 1; + } + + QHash *qm = new QHash; + + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + int state1, state2; + + TYPE1 *t1 = reinterpret_cast(sipConvertToType(t1obj, sipType_TYPE1, sipTransferObj, SIP_NOT_NONE, &state1, sipIsErr)); +#if PY_MAJOR_VERSION >= 3 + bool t2 = PyObject_IsTrue(t2obj); +#else + bool t2 = PyObject_IsTrue (t2obj); +#endif + + if (*sipIsErr) + { + sipReleaseType(t1, sipType_TYPE1, state1); + + delete qm; + return 0; + } + + qm->insert(*t1, t2); + + sipReleaseType(t1, sipType_TYPE1, state1); + } + + *sipCppPtr = qm; + + return sipGetState(sipTransferObj); +%End +""" +}, +"QVector": { #QVector +"code": +""" +%ConvertFromTypeCode + // Create the list. + PyObject *l; + + if ((l = PyList_New(sipCpp->size())) == NULL) + return NULL; + + // Set the list elements. + for (int i = 0; i < sipCpp->size(); ++i) + { + int t = (sipCpp->at(i)); + +#if PY_MAJOR_VERSION >= 3 + PyObject *tobj = PyLong_FromLong(t); +#else + PyObject *tobj = PyInt_FromLong(t); +#endif + + PyList_SET_ITEM(l, i, tobj); + } + + return l; +%End +%ConvertToTypeCode + // Check the type if that is all that is required. + if (sipIsErr == NULL) + { + if (!PyList_Check(sipPy)) + return 0; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) { + PyObject *tobj = PyList_GET_ITEM(sipPy, i); +#if PY_MAJOR_VERSION >= 3 + if (!PyNumber_Check(tobj)) +#else + if (!PyInt_Check(tobj)) +#endif + return 0; + } + return 1; + } + + QVector *qv = new QVector; + + for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i) + { + PyObject *tobj = PyList_GET_ITEM(sipPy, i); +#if PY_MAJOR_VERSION >= 3 + int t = PyLong_AsLong (tobj); +#else + int t = PyInt_AS_LONG (tobj); +#endif + + if (*sipIsErr) + { + delete qv; + return 0; + } + + qv->append(t); + } + + *sipCppPtr = qv; + + return sipGetState(sipTransferObj); +%End +""" +}, +# ./kdecore/ktimezone.sip +"KTimeZone": { #KTimeZone +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KTimeZone' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KSystemTimeZone; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTzfileTimeZone; +%End +""" +}, +"KTimeZoneBackend": { #KTimeZoneBackend +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KTimeZoneBackend' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KSystemTimeZoneBackend; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTzfileTimeZoneBackend; +%End +""" +}, +"KTimeZoneSource": { #KTimeZoneSource +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KTimeZoneSource' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KSystemTimeZoneSource; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KTzfileTimeZoneSource; +%End +""" +}, +# ./kdecore/kmacroexpander.sip +"kmacroexpander.h::KCharMacroExpander": { #KCharMacroExpander : KMacroExpanderBase +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KMacroExpanderBase' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KCharMacroExpander; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KWordMacroExpander; +%End +""" +}, +# ./dnssd/remoteservice.sip +"QList": { #QList + "code": list_typecode, + "value": { + "type": "DNSSD::RemoteService", + "ptr": "DNSSD::RemoteService::Ptr", + "HeldAs": HeldAs.POINTER, + }, +}, +# ./dnssd/servicebase.sip +"KDNSSD::ServiceBase": { #ServiceBase : KShared +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'ServiceBase' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + { + sipType = sipType_DNSSD_ServiceBase; + if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_PublicService; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_RemoteService; + } +%End +""" +}, +# ./dnssd/domainbrowser.sip +"KDNSSD::DomainBrowser": { #DomainBrowser : QObject +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QObject' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_DomainBrowser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_PublicService; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_RemoteService; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_ServiceBrowser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_ServiceTypeBrowser; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_DomainModel; + else if (dynamic_cast(sipCpp)) + sipType = sipType_DNSSD_ServiceModel; +%End +""" +}, +# ./kutils/dialog.sip +"Dialog": { #Dialog : KCMultiDialog +"code": +""" +%TypeHeaderCode +#include +#include +%End +""" +}, +# ./kterminal/kterminal.sip +"KTerminal": { #KTerminal +"code": +""" +%TypeHeaderCode +#include +#include +#include +%End +""" +}, +# ./kdeui/kpixmapcache.sip +"KPixmapCache": { #KPixmapCache +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KPixmapCache' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIconCache; +%End +""" +}, +# ./kdeui/kwidgetitemdelegate.sip +"QList": { #QList + "code": list_typecode, + "value": { + "type": "QEvent::Type", + "HeldAs": HeldAs.INTEGER, + }, +}, +# ./kdeui/kcursor.sip +"kcursor.h::KCursor": { #KCursor : QCursor +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QCursor' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KCursor; +%End +""" +}, +# ./kdeui/kapplication.sip +"KApplication": { #KApplication : QApplication +"code": +""" +%TypeCode +// Convert a Python argv list to a conventional C argc count and argv array. +static char **kdeui_ArgvToC(PyObject *argvlist, int &argc) +{ + char **argv; + + argc = PyList_GET_SIZE(argvlist); + + // Allocate space for two copies of the argument pointers, plus the + // terminating NULL. + if ((argv = (char **)sipMalloc(2 * (argc + 1) * sizeof (char *))) == NULL) + return NULL; + + // Convert the list. + for (int a = 0; a < argc; ++a) + { + char *arg; +#if PY_MAJOR_VERSION >= 3 + PyObject *utf8bytes = PyUnicode_AsUTF8String(PyList_GetItem(argvlist,a)); + arg = PyBytes_AsString(utf8bytes); +#else + arg = PyString_AsString(PyList_GetItem(argvlist,a)); +#endif + // Get the argument and allocate memory for it. + if (arg == NULL || + (argv[a] = (char *)sipMalloc(strlen(arg) + 1)) == NULL) + return NULL; + + // Copy the argument and save a pointer to it. + strcpy(argv[a], arg); + argv[a + argc + 1] = argv[a]; +#if PY_MAJOR_VERSION >= 3 + Py_DECREF(utf8bytes); +#endif + } + + argv[argc + argc + 1] = argv[argc] = NULL; + + return argv; +} + + +// Remove arguments from the Python argv list that have been removed from the +// C argv array. +static void kdeui_UpdatePyArgv(PyObject *argvlist, int argc, char **argv) +{ + for (int a = 0, na = 0; a < argc; ++a) + { + // See if it was removed. + if (argv[na] == argv[a + argc + 1]) + ++na; + else + PyList_SetSlice(argvlist, na, na + 1, NULL); + } +} +%End +""" +}, +# ./kdeui/kstandardaction.sip +"QList": { #QList + "code": list_typecode, + "value": { + "type": "KStandardAction::StandardAction", + "HeldAs": HeldAs.INTEGER, + }, +}, +# ./kdeui/kicon.sip +"KIcon": { #KIcon : QIcon +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'QIcon' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIcon; +%End +""" +}, +# ./kdeui/kiconcache.sip +"KIconCache": { #KIconCache : KPixmapCache +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KPixmapCache' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KIconCache; +%End +""" +}, +# ./kdeui/kconfigskeleton.sip +"kconfigskeleton.h::KConfigSkeleton": { #KConfigSkeleton : KCoreConfigSkeleton +"code": +""" +%ConvertToSubClassCode + // CTSCC for subclasses of 'KConfigSkeletonItem' + sipType = NULL; + + if (dynamic_cast(sipCpp)) + sipType = sipType_KConfigSkeleton_ItemColor; + else if (dynamic_cast(sipCpp)) + sipType = sipType_KConfigSkeleton_ItemFont; +%End +""" +}, +# ./kdeui/kwindowsystem.sip +"QList": { #QList + "code": list_typecode, + "value": { + "type": "WId", + "HeldAs": HeldAs.INTEGER, + }, +}, +} Index: find-modules/module_generation/PyKF5/gpgme.py =================================================================== --- /dev/null +++ find-modules/module_generation/PyKF5/gpgme.py @@ -0,0 +1,165 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. +# +""" +SIP binding customisation for PyKF5.gpgme__ and PyKF5.qgpgme. This modules describes: + + * Supplementary SIP file generator rules. +""" +import re + +import builtin_rules +import rule_helpers + + +def function_fix_callable(container, fn, sip, rule): + rule_helpers.initialise_cxx_decl(sip) + sip["parameters"][0] = sip["parameters"][0].replace("IdleFunction", "SIP_PYCALLABLE", 1) + sip["fn_result"] = "SIP_PYCALLABLE" + sip["code"] += """%MethodCode\n// TBD\n%End\n""" + + +def parameter_out(container, fn, parameter, sip, rule): + sip["decl"] = "const char **" + parameter.spelling + + +def module_fix_includes(filename, sip, rule): + rule_helpers.module_add_includes(filename, sip, rule, "", "", "") + rule_helpers.module_add_classes(filename, sip, rule, "_IO_FILE", "GpgME::VerificationResult::Private") + sip["code"] += "typedef int gpg_err_code_t;" + + +def module_fix_includes_if(filename, sip, rule): + rule_helpers.module_add_includes(filename, sip, rule, "", "") + + +def module_fix_interfaces(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "GpgME::Error", "GpgME::Data") + + +def module_fix_ordering(filename, sip, rule): + """ + SIP does not properly support forward declaration. So we physically yank + things into place. + """ + child = "^ class " + sip["ctx"]["child"] + "\n {.*?(^ };)$" + parent = "^ class " + sip["ctx"]["parent"] + ";$" + child = re.search(child, sip["decl"], re.DOTALL | re.MULTILINE) + tmp = sip["decl"][:child.start(0)] + "// Yanked from here" + sip["decl"][child.end(0):] + parent = re.search(parent, tmp, re.DOTALL | re.MULTILINE) + sip["decl"] = tmp[:parent.start(0)] + "// Yanked to here\n" + child.group(0) + "\n" + tmp[parent.end(0):] + + +def module_fix_types(filename, sip, rule): + rule_helpers.module_add_classes(filename, sip, rule, "_IO_FILE") + + +def container_rules(): + return [ + ["GpgME::Configuration", "Component", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["GpgME::Configuration", "Option", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["GpgME", "CreatedSignature|Import|Invalid(Recipient|SigningKey)|Key|Notation|Signature|Subkey|UserID", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ["GpgME::UserID", "Signature", ".*", ".*", ".*", rule_helpers.container_fake_derived_class], + ] + + +def forward_declaration_rules(): + return [ + ["global.h", "_GIOChannel", ".*", rule_helpers.noop], + ["gpgmefw.h", ".*", ".*", rule_helpers.noop], + ["GpgME::(Context|Data|(Signing|Import|Encryption)Result)", "Private", ".*", rule_helpers.noop], + ["GpgME", "Import", ".*", rule_helpers.noop], + ] + + +def function_rules(): + return [ + # + # Remove unsupported signature. + # + ["GpgME::Configuration::(Argument|Component|Option)", "operator void.*", ".*", ".*", ".*", rule_helpers.function_discard], + ["GpgME::Error", "operator void.*", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP needs help with std::basic_ostream. + # + ["GpgME.*", "operator<<", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP needs help with std::__cxx11::basic_string. + # + ["GpgME::Configuration::Option", "createString(|List)Argument", ".*", ".*", ".*", rule_helpers.function_discard], + ["GpgME::GpgAddUserIDEditInteractor", "set(Name|Email|Comment)Utf8", ".*", ".*", ".*", rule_helpers.function_discard], + ["GpgME::GpgAddUserIDEditInteractor", "(name|email|comment)Utf8", ".*", ".*", ".*", rule_helpers.function_discard], + ["GpgME::GpgSetExpiryTimeEditInteractor", "GpgSetExpiryTimeEditInteractor", ".*", ".*", ".*", rule_helpers.function_discard], + ["GpgME::DefaultAssuanTransaction", "data", ".*", ".*", ".*", rule_helpers.function_discard], + ["GpgME", "registerIdleFunction", ".*", ".*", ".*", function_fix_callable], + ["GpgME::(Context|Data)", "impl", ".*", ".*", ".*", ".*", "(?! const)", rule_helpers.function_discard], + ["GpgME::InvalidRecipient", "InvalidRecipient", ".*", ".*", ".*", builtin_rules.function_uses_templates], + ["GpgME::Import", "Import", ".*", ".*", ".*", builtin_rules.function_uses_templates], + ["GpgME::Notation", "Notation", ".*", ".*", ".*", builtin_rules.function_uses_templates], + ["GpgME::InvalidSigningKey", "InvalidSigningKey", ".*", ".*", ".*", builtin_rules.function_uses_templates], + ["GpgME::CreatedSignature", "CreatedSignature", ".*", ".*", ".*", builtin_rules.function_uses_templates], + ["GpgME::Signature", "Signature", ".*", ".*", ".*", builtin_rules.function_uses_templates], + # + # Get rid of de-facto private stuff. + # + ["GpgME::(Data|Key.*)", "Data|Key.*", ".*", ".*", ".*::Null.*", rule_helpers.function_discard], + ] + + +def parameter_rules(): + return [ + ["GpgME::Context", "startKeyListing|exportPublicKeys|startPublicKeyExport", "pattern(s|)", ".*\[\]", ".*", parameter_out], + ] + + +def variable_rules(): + return [ + # + # Get rid of de-facto private stuff. + # + ["GpgME::(Data|Key)", "null", ".*", rule_helpers.variable_discard], + ] + + +def modulecode(): + return { + "gpgme++/decryptionresult.h": { + "code": rule_helpers.module_yank_scoped_class, + "ctx": {"child": "Recipient", "parent": "DecryptionResult"}, + }, + "gpgme++/key.h": { + "code": rule_helpers.module_yank_scoped_class, + "ctx": {"child": "Signature", "parent": "UserID"}, + }, + "gpgme++/importresult.h": { + "code": module_fix_ordering, + "ctx": {"child": "Import", "parent": "Import"}, + }, + "gpgme++/interfaces/assuantransaction.h": { + "code": module_fix_includes_if, + }, + "gpgme__/interfaces/interfacesmod.sip": { + "code": module_fix_interfaces, + }, + "gpgme__/gpgme__mod.sip": { + "code": module_fix_includes, + }, + "qgpgme/qgpgmemod.sip": { + "code": module_fix_types, + }, + } Index: find-modules/module_generation/README =================================================================== --- /dev/null +++ find-modules/module_generation/README @@ -0,0 +1,250 @@ +HOWTO +===== + +See the separate HOWTO on how to build PyKF5 or your own bindings. + + +THE PROBLEM +=========== + +In the olden days, there was a set of Python bindings for Qt4, known as +PyQt(4) using a tool called SIP which requies as input .sip files which are +similar to the .h files they stand in for. People liked these, and so a +corresponding set of PyKDE(4) bindings was developed. When Qt5 came +out, it was followed by PyQt5, but the effort of maintaining the .sip files +for KF5 proved to be a roadblock for the KDE side of things. + +One of the key issues was the difficulty of maintaining a tool which could +(partially) automate the generation of .sip files from the .h files as the +C++ syntax evolved. + +This project aims to provide a fully automated process for: + + # The .sip generation step. + # Compiling the .sip files into ready-to-use bindings. + +The two steps are separate just in case the first step is not quite complete +:-). The .sip files and bindings are intended to be shippable as-is. + + +THE SOLUTION +============ + +The key points are: + + # Using the Python binding to Clang, build tools which use a rules-driven + approach to generating the .sip files for *any* C++ project (with a + minimal bias to Qt/KDE projects). It is expected that Clang will likely + be able to keep up as C++ evolves. + # The rules are defined on a "project" (think KF5) basis using regular + expressions to define where 2-3 line code fragments to do all + customisations and any injection of manual code. + # Allow manual code to be cleanly injected into the generated code. All + useful bits of syntax are supplemented by rules which can be used to + perform arbitrary modifications of the automatically generated code. + # Support for automatically generating %MappedType from a templating system + to facilitate the use of C++ templates (e.g. functions taking QList<>s). + # (TODO) Figure out if something smart/automatic can be done for + %ConvertToSubClassCode. + +sip_generator.py, rules_engine.py and rules_xxx.py +-------------------------------------------------- + +The core logic is in sip_generator.py. The purpose is to generate a .sip file +content from a .h file, using a project-specific rules_xxx.py. Typically, the +rules: + + # Fixup bits of syntax the core logic did not get right. + # Discard syntax which SIP cannot handle. + # Inject any custom code. + +The sip_generator has few references to anything Qt or KDE specific (these +should be generally non-intrusive, and they could be factored out if needed). +The CLI defaults are suitable for KF5, and will need overriding for other +projects. + +For normal sized projects with a sane number of .h files, this may be all you +need, along with your custom rules_xxx.py. + + +module_generator.py +------------------- + +The next layer on the generator side is module_generator.py. This takes the +output from sip_generator for a whole tree of .h files and renders then into +"individual" .sip files. + +First, various types of "individual" .h files are handled. We either: + + # Generate a non-empty "normal" .sip files containing useful definitions. + # Detect an empty .h file which contains exactly one "#include". This is + understood to be a "forwarding" header, and generate a "forwarding" + .sip. + # (TODO) Detect a _version.h file and generate appropriate .py code. + +At this stage, there is a close 1:1 mapping between the .h hierachy and +the .sip hierarchy. + +Then, for each set of "individual" .sip files in a directory ("normal" or +"forwarding"), we generate a "module" .sip. This %Includes the "individual" +.sip files into a %Module, which translates the directory-based namespace on +the .h side into a Python package (and mirrors what PyQt does). + +Any %MappedTypes are appended to the %Module file, removing any duplicates +that may have been generated from the processing of the "individual" .sip files. + +So far, all this is automatic, driven purely off the .h file directory +hierarchy. + +Finally, rules are then used to select a set of the "module" .sip files (e.g. +you might only want the ones which conform to the new namespace-style +convention) to actually generate binding C++ code. In the case of KF5, this +reduces the ~2000 "individual" .sip files to ~120 .so files which are the new, +namespaced, bindings which support the "from PyKF5 import KDBusAddons" idiom. + +The module_generator has no references to anything Qt or KDE specific, but +does exploit certain patterns that these projects observe, and which are +expected to be generally applicible. The CLI defaults are suitable for KF5, +and will need overriding for other projects. + + +module_compiler.py +------------------ + +This actualy runs the SIP compler, and then the C++ compiler to create the +bindings. + +The module_compiler side should be fully Qt/KDE agnostic. The CLI defaults are +suitable for KF5, and will need overriding for other projects. + + +HOW IT WORKS +============ + +The sip_generator uses Clang, i.e. a real compiler front end, to parse the +code into an internal representation exposed through a Python binding. This is +then walked using the binding to generate the .sip output. + +The walk is split into several categories: + + # Containers (the top level "translation unit", C++ namespaces, classes + structs, unions). + # Functions, including method and constructors. + # Function parameters. + # enums, typedefs, variable + +For each item in each category, and before the corresponding output is +emitted, the rules_engine is consulted for a matching rule using a system +based on regular expressions; the first matching rule found invokes a +project-defined function which can do pretty much anything imaginable to the +item. + +The module_generator is where things get interesting. As noted above, it +runs th module_compiler over an entire directory tree, and so it has to solve +certain problems that arise when packaging the "individual" .sip files +using the "module" .sip files. + + +Recursive %Import clauses +------------------------- + +It is common in C++ code to have #includes which imply a recursive +relationship at the SIP module level. For example: + + * KIOCore/kidskfreespaceinfo.h depends on kio/global.h + * KIOCore/kio/directorysizejob.h depends on KIOCore/kfileitem.h + +which implies that KIOCore/KIOCoremod.sip and KIOCore/kio/kiomod.sip need to +%Import each other. This is not supported (or likely to be supported) by SIP. +We solve this by having module_generator first: + + # Wrap each %Import in a %Feature. + # Create a project-wide list of features in a file "module.features". + +and then the module_compiler uses the SIP compiler's -x (lower case X) option +to disable the %Feature which guards the unwanted %Import, thus breaking the +recursion. + + +Automatic -I generation +----------------------- + +In order for the code generated by the SIP compiler to be successfully +compiled by the C++ compiler, it is typically necessary to specify a set of +-I options. Now the exact set of these needed is known to module_compiler.py, +so it stashes the needed paths using the SIP compiler's %Extract facility. +Thus, the path data is not seen by the SIP compiler, yet can be extracted +by the module_compiler using the SIP compiler's -X (upper case X) option when +it is run. + +After the SIP compiler has been run, the extracted path data is read and then +passed as -I options into the C++ compiler. + + +Renaming headers +----------------- + +A common pattern in C++ headers is to have a "legacy" header pointed to by a +"forwarding" header. The latter #includes the former. This can easily be +rendered in SIP using a %Include, but that then causes the following practical +issues: + + * The forwarding SIP's binding depends on the legacy SIP's binding at + both link time (which is annoying to automate) and run time (doubling + the number of .so files). + * The two sets of bindings have names which usually differ only in case + (lowercase versus CamelCase) which is a problem on some filesystems, + and causes confusion. + * It perpetuates the presence of the legacy binding (PyKDE4 -> PyKF5 + already requires any software being ported to revisit their import + clauses, so this is the perfect moment to break that chain). + +Instead, we just render the forwarding SIP as a copy of the legacy/real +content. + + +Duplicate forward declarations +------------------------------ + +Unlike C++, SIP expects exactly one declaration/definition of a class in any +%Module. This means that: + +- Forward declarations for classes in the same %Module are not allowed. + +- Multiple forward declaration are not allowed even for classes in other +%Modules. + +The solution is to suppress all forward declarations by default. Rule-writers +selectively use a noop-rule to override the supression, or add %Module level +declarations to replace them (even then, %feature-based guards are needed +to avoid duplicates across separate %Modules!). Helpers are provided for both. + +Template support +---------------- + +SIP has a form of support for template classes. Unfortunately, this typically +requires multiple sets of templates to handle plain-old-data versus object +template parameters. + +The solution is rule-based code which generates "template instantiations" +using types inferred from the .h content. Logic is provided for the main +Qt containers, QFlags and some std::/boost:: classes. + +The logic for this has been factored out to facilitate adding code for +additional template types. + + +Platform-dependent code +----------------------- + +There are several aspects of the system which are platform-dependent. These +are carefully isolated using CMake; the user is free to implement whatever +platform-dependent logic is needed in the callouts to CMake: + + * There is a CMakeLists.txt and some supporting .cmake files included + in the base code. This is used to locate SIP and Clang components. + + * The rules can make use of CMake to locate header files, libraries and + so on; see the example rules supplied and the helper logic in + rules_engine.py + Index: find-modules/module_generation/builtin_rules.py =================================================================== --- /dev/null +++ find-modules/module_generation/builtin_rules.py @@ -0,0 +1,689 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +Rules which ameliorate some of the shortcomings of SIP. + +The main public members of this module are intended to be used in a +composable manner. For example, a variable array which is also extern +could be handled by calling @see variable_rewrite_array_nonfixed() and then +@see variable_rewrite_extern(). +""" +import gettext +import logging +import re +from clang.cindex import CursorKind, StorageClass, TypeKind + +import clangcparser +import rule_helpers +import utils +from utils import trace_generated_for, HeldAs +from templates.methodcode import function_uses_templates + +logger = logging.getLogger(__name__) +gettext.install(__name__) + +# Keep PyCharm happy. +_ = _ + + +MAPPED_TYPE_RE = re.compile(".*<.*") + + +class RewriteArrayHelper(HeldAs): + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + options = { + HeldAs.INTEGER: + """ {name} = {cxx_to_py_value}; +""", + HeldAs.FLOAT: + """ {name} = {cxx_to_py_value}; +""", + HeldAs.POINTER: + """ int {name}State; + Cxx{name}T cxx{name} = {cxx_to_py_value}; +""", + HeldAs.OBJECT: + """ int {name}State; + Cxx{name}T *cxx{name} = {cxx_to_py_value}; +""", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{cxx_to_py_value}", cxx_to_py_value) + return code + + def py_to_cxx_template(self, name, py_v, py_to_cxx_value): + options = { + HeldAs.INTEGER: + """ {cxx_v} = {py_to_cxx_value}; +""", + HeldAs.FLOAT: + """ {cxx_v} = {py_to_cxx_value}; +""", + HeldAs.POINTER: + """ int {name}State; + Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.OBJECT: + """ int {name}State; + Cxx{name}T *cxx{name} = {py_to_cxx_value}; +""", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", py_v) + code = code.replace("{py_to_cxx_value}", py_to_cxx_value) + return code + + def py_to_cxx_value(self, name, py_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong({name}) +#else + (Cxx{name}T)PyInt_AsLong({name}) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble({name})", + HeldAs.POINTER: + "reinterpret_cast(sipForceConvertToType({name}, gen{name}T, sipTransferObj, SIP_NOT_NONE, &{name}State, sipIsErr))", + HeldAs.OBJECT: + "reinterpret_cast(sipForceConvertToType({name}, gen{name}T, sipTransferObj, SIP_NOT_NONE, &{name}State, sipIsErr))", + } + ptr_options = { + HeldAs.BYTE: + "(Cxx{name}T)PyString_AsString({name})", + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong(*({name})) +#else + (Cxx{name}T)PyInt_AsLong(*({name})) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble(*({name}))", + } + + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{py_v}", py_v) + return code + + +class RewriteMappedHelper(HeldAs): + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + options = { + HeldAs.INTEGER: + """ PyObject *{name} = {cxx_to_py_value}; +""", + HeldAs.FLOAT: + """ PyObject *{name} = {cxx_to_py_value}; +""", + HeldAs.POINTER: + """ PyObject *{name} = {cxx_to_py_value}; +""", + HeldAs.OBJECT: + """ PyObject *{name} = {cxx_to_py_value}; +""", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{cxx_to_py_value}", cxx_to_py_value) + return code + + def cxx_to_py_value(self, name, cxx_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + PyLong_FromLong((long){cxx_v}) +#else + PyInt_FromLong((long){cxx_v}) +#endif +)""", + HeldAs.FLOAT: + "PyFloat_FromDouble((double){cxx_v})", + HeldAs.POINTER: + "sipConvertFromType((void *){cxx_v}, gen{name}T, {transfer})", + HeldAs.OBJECT: + "sipConvertFromType((void *)&{cxx_v}, gen{name}T, {transfer})", + } + ptr_options = { + HeldAs.BYTE: + "PyString_FromStringAndSize((char *)({cxx_v}), 1)", + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + PyLong_FromLong((long)*{cxx_v}) +#else + PyInt_FromLong((long)*{cxx_v}) +#endif +)""", + HeldAs.FLOAT: + "PyFloat_FromDouble((double)*({cxx_v}))", + } + + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{transfer}", transfer) + return code + + def py_to_cxx_template(self, name, py_v, py_to_cxx_value): + options = { + HeldAs.INTEGER: + """ Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.FLOAT: + """ Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.POINTER: + """ int {name}State; + Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.OBJECT: + """ int {name}State; + Cxx{name}T *cxx{name} = {py_to_cxx_value}; +""", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", py_v) + code = code.replace("{py_to_cxx_value}", py_to_cxx_value) + return code + + def py_to_cxx_value(self, name, py_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong({name}) +#else + (Cxx{name}T)PyInt_AsLong({name}) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble({name})", + HeldAs.POINTER: + "reinterpret_cast(sipForceConvertToType(sipPy, gen{name}T, {transfer}, SIP_NOT_NONE, &{name}State, &sipErr))", + HeldAs.OBJECT: + "reinterpret_cast(sipForceConvertToType(sipPy, gen{name}T, {transfer}, SIP_NOT_NONE, &{name}State, &sipErr))", + } + ptr_options = { + HeldAs.BYTE: + "(Cxx{name}T)PyString_AsString({name})", + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong(*({name})) +#else + (Cxx{name}T)PyInt_AsLong(*({name})) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble(*({name}))", + } + + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{py_v}", py_v) + code = code.replace("{transfer}", transfer) + return code + + +def container_rewrite_exception(container, sip, rule): + """ + Convert a class which is an exception into a %Exception. + + :param container: + :param sip: + :param matcher: + :return: + """ + sip["name"] = utils.fqn(container, sip["name"]) + sip_name = sip["name"].replace("::", "_") + py_name = "".join([w[0].upper() + w[1:] for w in sip_name.split("_")]) + base_exception = sip["base_specifiers"][0] + sip["decl"] = "%Exception {}({}) /PyName={}/".format(sip["name"], base_exception, py_name) + sip["base_specifiers"] = [] + sip["body"] = """ +%RaiseCode + const char *detail = sipExceptionRef.what(); + + SIP_BLOCK_THREADS + PyErr_SetString(sipException_{}, detail); + SIP_UNBLOCK_THREADS +%End +""".format(sip_name) + + +def container_rewrite_std_exception(container, sip, rule): + """ + Synthesise an %Exception for std::. + """ + std_exception = sip["base_specifiers"][0] + container_rewrite_exception(container, sip, rule) + sip_name = std_exception.replace("::", "_") + py_name = "".join([w[0].upper() + w[1:] for w in sip_name.split("_")]) + sip["modulecode"][std_exception] = """%Exception {}(SIP_Exception) /PyName={}/ +{{ +%TypeHeaderCode +#include +%End + +%RaiseCode + const char *detail = sipExceptionRef.what(); + + SIP_BLOCK_THREADS + PyErr_SetString(sipException_{}, detail); + SIP_UNBLOCK_THREADS +%End +}}; +""".format(std_exception, py_name, sip_name) + + +def variable_rewrite_array(container, variable, sip, rule): + if variable.type.kind == TypeKind.CONSTANTARRAY: + variable_rewrite_array_fixed(container, variable, sip, rule) + else: + variable_rewrite_array_nonfixed(container, variable, sip, rule) + + +def variable_rewrite_array_fixed(container, variable, sip, rule): + """ + Handle n-dimensional fixed size arrays. + """ + # + # Templates for a SIP_PYBUFFER. + # + SIP_PYBUFFER_GETCODE = """ char *cxxvalue = (char *)&{cxxarray}[0]; + + // Create the Python buffer. + Py_ssize_t elementCount = {element_count}; + sipPy = PyByteArray_FromStringAndSize(cxxvalue, elementCount);""" + + SIP_PYBUFFER_SETCODE = """ char *cxxvalue = (char *)&{cxxarray}[0]; + Py_ssize_t elementCount = {element_count}; + const char *name = "{name}"; + + if (!PyByteArray_Check(sipPy)) { + PyErr_Format(PyExc_TypeError, "expected buffer"); + sipErr = 1; + } + + // Convert the buffer to C++. + if (!sipErr) { + if (PyByteArray_GET_SIZE(sipPy) != elementCount) { + PyErr_Format(PyExc_ValueError, "'%s' must have length %ld", name, elementCount); + sipErr = 1; + } else { + memcpy(cxxvalue, PyByteArray_AsString(sipPy), elementCount); + } + }""" + # + # Templates for a SIP_PYLIST. + # + SIP_PYLIST_GETCODE = """{aliases} +struct getcode +{ + PyObject *getList(int *sipErr, Py_ssize_t dims[], int numDims, int dim, CxxvalueT *cxxvalue[]) { + Py_ssize_t elementCount = dims[dim]; + PyObject *list; + + // Create the Python list. + if (!*sipErr) { + list = PyList_New(elementCount); + if (!list) { + PyErr_Format(PyExc_TypeError, "unable to create a list"); + *sipErr = 1; + } + } + + // Set the list elements. + if (!*sipErr) { + for (Py_ssize_t i = 0; i < elementCount; ++i) { + PyObject *value; + if (dim + 1 < numDims) { + value = getList(sipErr, dims, numDims, dim + 1, &cxxvalue[i]); + } else { +// TODO: use sipConvertToArray and sipConvertToTypedArray +{cxx_to_py} + } + if (value == NULL) { + PyErr_Format(PyExc_TypeError, "cannot insert value into list"); + Py_XDECREF(value); + Py_DECREF(list); + *sipErr = 1; + } else { + PyList_SET_ITEM(list, i, value); + } + } + } + return list; + }; +} getcode; + + Py_ssize_t dims[] = {{dims}}; + auto numDims = sizeof(dims) / sizeof(dims[0]); + int sipErr = 0; + PyObject *list = getcode.getList(&sipErr, dims, numDims, 0, (CxxvalueT **)&{cxxarray}[0]); + sipPy = sipErr ? list : NULL;""" + + SIP_PYLIST_SETCODE = """typedef {cxx_t} CxxvalueT; +struct setcode +{ + void setList(int *sipErr, Py_ssize_t dims[], int numDims, int dim, PyObject *list, CxxvalueT *cxxvalue[]) { + Py_ssize_t elementCount = dims[dim]; + const char *name = "{name}"; + + if (!*sipErr) { + if (!PyList_Check(list)) { + PyErr_Format(PyExc_TypeError, "expected list"); + *sipErr = 1; + } + } + + // Convert the list to C++. + if (!*sipErr) { + if (PyList_GET_SIZE(list) != elementCount) { + PyErr_Format(PyExc_ValueError, "'%s' must have length %ld", name, elementCount); + *sipErr = 1; + } else { + for (Py_ssize_t i = 0; i < elementCount; ++i) { + PyObject *value = PyList_GetItem(list, i); + if (dim + 1 < numDims) { + setList(sipErr, dims, numDims, dim + 1, value, &cxxvalue[i]); + } else { +{py_to_cxx} + } + } + } + } + }; +} setcode; + + Py_ssize_t dims[] = {{dims}}; + auto numDims = sizeof(dims) / sizeof(dims[0]); + setcode.setList(&sipErr, dims, numDims, 0, sipPy, (CxxvalueT **)&{cxxarray}[0]);""" + + prefixes, text, operators, suffixes = utils.decompose_type(sip["decl"]) + dims = [d[1:-1] for d in suffixes if d[0] == "["] + element_type = variable.type + while element_type.kind == TypeKind.CONSTANTARRAY: + element_type = element_type.underlying_type + converter = RewriteArrayHelper(text, element_type) + if converter.category == HeldAs.BYTE: + decl = "SIP_PYBUFFER" + getcode = SIP_PYBUFFER_GETCODE + setcode = SIP_PYBUFFER_SETCODE + else: + decl = "SIP_PYLIST" + getcode = SIP_PYLIST_GETCODE + setcode = SIP_PYLIST_SETCODE + code = """ +{ +%GetCode +{trace} +""" + code += getcode + code += """ +%End""" + # + # Do we need %SetCode? + # + if "const " not in prefixes: + code += """ + +%SetCode +{trace} +""" + code += setcode + code += """ +%End""" + code += """ +}""" + if converter.category == HeldAs.BYTE: + pass + else: + aliases_ = converter.declare_type_helpers("value", "*sipErr = 1;") + cxx_to_py = converter.cxx_to_py("value", True, "(*cxxvalue)[i]") + py_to_cxx = converter.py_to_cxx("value", True, "(*cxxvalue)[i]") + code = code.replace("{cxx_t}", element_type.spelling) + code = code.replace("{aliases}", aliases_) + code = code.replace("{cxx_to_py}", cxx_to_py) + code = code.replace("{py_to_cxx}", py_to_cxx) + trace = trace_generated_for(variable, rule, {}) + code = code.replace("{trace}", trace) + if container.kind == CursorKind.TRANSLATION_UNIT: + if len(dims) == 1 and converter.category == HeldAs.BYTE: + # + # Note that replacing [] with * only works for one dimension. + # + dims = "*" * len(dims) + sip["decl"] = re.sub("\[.*\]", dims, sip["decl"]) + else: + # + # SIP does not support %GetCode/%SetCode for global variables. + # + logger.warning( + _("SIP does not support global array variables: {}").format(utils.item_describe(variable))) + sip["name"] = "" + else: + sip["decl"] = decl + code = code.replace("{element_count}", dims[-1]) + if variable.storage_class == StorageClass.STATIC: + code = code.replace("{cxxarray}", utils.fqn(variable, "{name}")) + else: + code = code.replace("{cxxarray}", "sipCpp->{name}") + code = code.replace("{name}", sip["name"]) + dims = ["(Py_ssize_t){}".format(i) for i in dims] + code = code.replace("{dims}", ", ".join(dims)) + sip["code"] = code + + +def variable_rewrite_array_nonfixed(container, variable, sip, rule): + prefixes, text, operators, dims = utils.decompose_type(sip["decl"]) + if len(dims) == 1: + sip["decl"] = sip["decl"].replace("[]", "*") + else: + logger.warning( + _("TODO: Support for nonfixed {}-D variables: {}").format(len(dims), utils.item_describe(variable))) + # + # Even though we render this as a "foo *", it started life as a "foo []". So if there is a const, the array + # itself was a const... + # + if "const " in prefixes: + sip["annotations"].add("NoSetter") + + +def variable_rewrite_extern(container, variable, sip, rule): + """ + SIP does not support "extern", so drop the keyword. + """ + sip["decl"] = sip["decl"][7:] + if MAPPED_TYPE_RE.match(sip["decl"]): + variable_rewrite_mapped(container, variable, sip, rule) + elif isinstance(variable.type, clangcparser.ArrayType): + variable_rewrite_array(container, variable, sip, rule) + + +def variable_rewrite_static(container, variable, sip, rule): + """ + SIP does not support "static", so handle static variables. + """ + # + # SIP does not support %GetCode/%SetCode for file scope variables. But luckily, just dropping the prefix works + # assuming we don't actually need anything more complicated. + # + sip["decl"] = sip["decl"][7:] + if MAPPED_TYPE_RE.match(sip["decl"]): + variable_rewrite_mapped(container, variable, sip, rule) + elif isinstance(variable.type, clangcparser.ArrayType): + variable_rewrite_array(container, variable, sip, rule) + # + # Inside a class, "static" is fine. Do we need %GetCode/%SetCode? + # + while container.kind == CursorKind.NAMESPACE: + container = container.semantic_parent + if container.kind != CursorKind.TRANSLATION_UNIT: + sip["decl"] = "static " + sip["decl"] + + +def variable_rewrite_mapped(container, variable, sip, rule): + """ + Handle class-static variables. + """ + # + # Create a Python <-> C++ conversion helper. + # + variable_type = variable.type.get_canonical() + converter = RewriteMappedHelper(variable_type.spelling, variable_type) + aliases = converter.declare_type_helpers("value", "sipErr = 1;") + code = """ +{ +%GetCode +{trace} + int sipErr = 0; +""" + code += aliases + if converter.is_mapped_type: + code += """ if (sipErr) { + return NULL; + } +""" + is_complex = converter.category in [HeldAs.POINTER, HeldAs.OBJECT] + if variable.storage_class == StorageClass.STATIC: + cxx = utils.fqn(variable, "{name}") + else: + cxx = "sipCpp->{name}" + code += converter.cxx_to_py("value", False, "{cxx}") + code += """ sipPy = (sipErr || PyErr_Occurred()) ? NULL : value; +%End + +%SetCode +{trace} +""" + code += aliases + if converter.is_mapped_type: + code += """ if (sipErr) { + return -1; + } +""" + code += converter.py_to_cxx("value", False, "{cxx}") + if is_complex: + code += """ if (!sipErr) { + {cxx} = *cxxvalue; + } +%End +}""" + else: + code += """ if (PyErr_Occurred()) { + sipErr = 1; + } else { + {cxx} = cxxvalue; + } +%End +}""" + code = code.replace("{cxx}", cxx) + code = code.replace("{name}", sip["name"]) + trace = trace_generated_for(variable, rule, {}) + code = code.replace("{trace}", trace) + sip["code"] = code + + +def container_rules(): + return [ + # + # Handle std:: exceptions. + # + [".*", ".*Exception", ".*", ".*", "std::.*", container_rewrite_std_exception], + # + # Other exceptions. + # + [".*", ".*", ".*", ".*", ".*Exception", container_rewrite_exception], + ] + + +def forward_declaration_rules(): + return [ + # + # Default forward declaration handling. + # + [".*", ".*", ".*", rule_helpers.forward_declaration_discard], + ] + + +def function_rules(): + return [ + # + # Handle functions with templated return types and/or templated parameters. + # + [".*", ".*", ".*", ".*[A-Za-z0-9_:]+<.*>.*", ".*", function_uses_templates], + [".*", ".*", ".*", ".*", ".*[A-Za-z0-9_:]+<.*>.*", function_uses_templates], + ] + + +def variable_rules(): + return [ + # + # Discard the extern keyword. + # + [".*", ".*", "extern .*", variable_rewrite_extern], + # + # Emit code for static variables. + # + [".*", ".*", "static .*", variable_rewrite_static], + # + # Emit code for templated variables. + # + [".*", ".*", MAPPED_TYPE_RE.pattern, variable_rewrite_mapped], + # + # Emit code for arrays. + # + [".*", ".*", "(const | volatile )*.*(\[[^]]*\])+", variable_rewrite_array], + ] Index: find-modules/module_generation/clangcparser.py =================================================================== --- /dev/null +++ find-modules/module_generation/clangcparser.py @@ -0,0 +1,528 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +"""A Clang-C wrapper that irons out some of the idiosyncrasies of Clang-C.""" +import gettext +import logging +import re + +import clang.cindex +from clang.cindex import Type as _Type + +import clangcplus +import utils + +logger = logging.getLogger(__name__) +gettext.install(__name__) + +# Keep PyCharm happy. +_ = _ + +CursorKind = clangcplus.CursorKind +TypeKind = clangcplus.TypeKind + + +class Cursor(clangcplus.Cursor): + CLASS_MAP = {} + + def get_children(self): + """ + Get the children of this Cursor either as Cursors, or as clang.cindex.Cursor. + """ + template_parameter_number = -1 + parameter_number = -1 + for child in self.proxied_object.get_children(): + try: + kind = child.kind + except ValueError as e: + # + # TODO: Some kinds result in a Clang error. + # + logger.debug(_("Unknown _kind_id {} for {}".format(e, child.spelling or child.displayname))) + else: + if kind in ParameterCursor.CURSOR_KINDS: + parameter_number += 1 + yield ParameterCursor(child, parameter_number) + elif kind in TemplateParameterCursor.CURSOR_KINDS: + template_parameter_number += 1 + yield TemplateParameterCursor(child, template_parameter_number) + else: + yield self._wrapped(child) + + +class ContainerCursor(clangcplus.ContainerCursor, Cursor): + # + # Our CURSOR_KINDS only adds templates. + # + TEMPLATE_CURSOR_KINDS = [CursorKind.CLASS_TEMPLATE, CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION] + CURSOR_KINDS = clangcplus.ContainerCursor.CURSOR_KINDS + TEMPLATE_CURSOR_KINDS + + def __init__(self, container): + super(ContainerCursor, self).__init__(container) + # + # If this is an array, the container is templated. + # + self.template_parameters = None + # + # Assume this is a class, not a struct. + # + self.initial_access_specifier = "" + if container.kind in self.TEMPLATE_CURSOR_KINDS: + # + # Clang presents a templated struct as a CLASS_TEMPLATE, but does not + # insert an initial "public" access specifier. Make a best-effort attempt + # to find this (container.get_tokens() can be flummoxed by macros etc.). + # + found_start = False + found_end = False + bracket_level = 0 + for token in container.get_tokens(): + # + # Now count balanced <> till we get to the end. + # + if bracket_level == 0 and found_start and token.kind == clang.cindex.TokenKind.KEYWORD: + found_end = True + if token.spelling == "struct": + self.initial_access_specifier = "public: // Was struct" + break + elif token.spelling in "<": + found_start = True + bracket_level += 1 + elif token.spelling in ">": + bracket_level -= 1 + if not found_start or not found_end: + # + # Clang tokens did not help. Try looking in the source code. + # + text = self.translation_unit.source_processor.preprocessed(container.extent, + container.extent.start.file.name) + found = text.find(container.spelling) + if found != -1: + text = text[:found].strip() + if text.endswith("struct"): + self.initial_access_specifier = "public: // Was struct" + else: + # + # The worst case is that the user has to write a rule to fix this. + # + logger.debug(_("Unable to expand {}".format(container.spelling))) + + @property + def SIP_TYPE_NAME(self): + return "namespace" if self.kind == CursorKind.NAMESPACE else "class" + + +class EnumCursor(ContainerCursor): + CURSOR_KINDS = [CursorKind.ENUM_DECL] + SIP_TYPE_NAME = "enum" + GENERATED_NAME_FMT = "__enum{}" + PROXIES = ( + clang.cindex.Cursor, + [ + "type" + ] + ) + + @property + def spelling(self): + return self.proxied_object.spelling or self.proxied_object.displayname or \ + self.GENERATED_NAME_FMT.format(self.extent.start.line) + + +class FunctionCursor(Cursor): + PROXIES = ( + clang.cindex.Cursor, + [ + "get_arguments", "get_definition", "get_num_template_arguments", "get_template_argument_kind", + "get_template_argument_type", "get_template_argument_unsigned_value", "get_template_argument_value", + "is_const_method", "is_converting_constructor", "is_copy_constructor", "is_default_constructor", + "is_default_method", "is_definition", "is_move_constructor", "is_pure_virtual_method", "is_static_method", + "is_virtual_method", + ] + ) + CURSOR_KINDS = [CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.FUNCTION_TEMPLATE, + CursorKind.CONSTRUCTOR, CursorKind.DESTRUCTOR, CursorKind.CONVERSION_FUNCTION] + + def __init__(self, fn): + super(FunctionCursor, self).__init__(fn) + # + # If this is an array, the function is templated. + # + self.template_parameters = None + + def is_implementation(self, container): + """ + Is implementation of function previously declared in a class/struct. + + :param container: The current container, which is not + necessarily the semantic_parent; it is this + distinction which matters. + """ + return self.proxied_object.is_definition() and \ + container.kind in [CursorKind.TRANSLATION_UNIT, CursorKind.NAMESPACE] and \ + self.semantic_parent.kind in [CursorKind.CLASS_DECL, CursorKind.STRUCT_DECL] + + @property + def result_type(self): + return Type._wrapped(self.proxied_object.result_type) + + +class ParameterCursor(Cursor): + CURSOR_KINDS = [CursorKind.PARM_DECL] + + def __init__(self, parameter, parameter_number): + super(ParameterCursor, self).__init__(parameter) + self.parameter_number = parameter_number + + @property + def spelling(self): + return self.proxied_object.spelling or "__{}".format(self.parameter_number) + + @property + def type(self): + return Type._wrapped(self.proxied_object.type) + + @property + def SIP_TYPE_NAME(self): + the_type = self.type.get_canonical() + type_spelling = the_type.spelling + # + # Get rid of any pointer const-ness and add a pointer suffix. Not removing the const-ness causes + # SIP to generate sequences which the C++ compiler seems to optimise away: + # + # QObject* const a1 = 0; + # + # if (sipParseArgs(..., &a1)) + # + if the_type.get_canonical().is_a_function: + # + # SIP does not generally like function pointers. Here the problem + # is that parameters just don't support canonical function pointers + # with default values, so use the typedef if one is known. Else, + # rules are needed to fix them up. + # + if self.type.spelling.find("(") == -1: + decl = self.type.spelling + if decl[-1] not in "*&": + decl += " " + decl = decl + self.spelling + else: + the_type = the_type.get_canonical() + args = the_type.fmt_args() + name = the_type.fmt_name(self.spelling) + result = the_type.fmt_result() + decl = "{}({})({})".format(result, name, args) + elif the_type.kind == TypeKind.INCOMPLETEARRAY: + # + # Clang makes "const int []" into "int const[]"!!! + # + if " const[" in type_spelling: + type_spelling = "const " + type_spelling.replace(" const[", " [", 1) + decl = type_spelling.replace("[", " " + self.spelling + "[", 1) + else: + decl = "{} {}".format(type_spelling, self.spelling) + decl = decl.replace("* ", "*").replace("& ", "&") + return decl + + +class TemplateParameterCursor(Cursor): + CURSOR_KINDS = [CursorKind.TEMPLATE_TYPE_PARAMETER, CursorKind.TEMPLATE_NON_TYPE_PARAMETER, + CursorKind.TEMPLATE_TEMPLATE_PARAMETER] + + def __init__(self, parameter, parameter_number): + super(TemplateParameterCursor, self).__init__(parameter) + self.parameter_number = parameter_number + + @property + def spelling(self): + return self.proxied_object.spelling or "__{}".format(self.parameter_number) + + @property + def type(self): + return Type._wrapped(self.proxied_object.type) + + @property + def SIP_TYPE_NAME(self): + if self.kind == CursorKind.TEMPLATE_TYPE_PARAMETER: + # + # "typename " + self.spelling + # + return self.spelling + elif self.kind == CursorKind.TEMPLATE_NON_TYPE_PARAMETER: + # + # self.type.spelling + " " + self.spelling + # + return self.spelling + elif self.kind == CursorKind.TEMPLATE_TEMPLATE_PARAMETER: + # + # Recursive template template parameter walk... + # + # template<...> class foo + # + parameter = [c.SIP_TYPE_NAME for c in self.get_children()] + return "template<" + (", ".join(parameter)) + "> class " + self.spelling + + +class StructCursor(EnumCursor): + CURSOR_KINDS = [CursorKind.STRUCT_DECL] + SIP_TYPE_NAME = "struct" + GENERATED_NAME_FMT = "__struct{}" + + +class UnionCursor(EnumCursor): + CURSOR_KINDS = [CursorKind.UNION_DECL] + # + # Render a union as a struct. From the point of view of the accessors created for the bindings, + # this should behave as expected! + # + SIP_TYPE_NAME = "/* union */ struct" + GENERATED_NAME_FMT = "__union{}" + + +class TranslationUnitCursor(clangcplus.TranslationUnitCursor, Cursor): + pass + + +class TypedefCursor(Cursor): + CURSOR_KINDS = [CursorKind.TYPEDEF_DECL] + + @property + def type(self): + return Type._wrapped(self.proxied_object.type) + + @property + def result_type(self): + return Type._wrapped(self.proxied_object.result_type) + + @property + def underlying_type(self): + return Type._wrapped(self.proxied_object.underlying_typedef_type) + + @property + def SIP_TYPE_NAME(self): + the_type = self.underlying_type + type_spelling = the_type.spelling + if the_type.get_canonical().is_a_function: + the_type = the_type.get_canonical() + decl = the_type.fmt_args() + elif the_type.kind == TypeKind.RECORD: + decl = type_spelling + elif the_type.kind == TypeKind.DEPENDENTSIZEDARRAY: + # + # Clang makes "QString foo[size]" into "QString [size]"!!! + # + decl = type_spelling.replace("[", self.spelling + "[", 1) + else: + decl = the_type.get_canonical().spelling + return decl + + +class TemplaterefCursor(Cursor): + CURSOR_KINDS = [CursorKind.TEMPLATE_REF] + + @property + def type(self): + return Type._wrapped(self.proxied_object.type) + + @property + def semantic_parent(self): + return None + + +class TyperefCursor(Cursor): + CURSOR_KINDS = [CursorKind.TYPE_REF] + + @property + def type(self): + return Type._wrapped(self.proxied_object.type) + + @property + def semantic_parent(self): + return None + + +class VariableCursor(Cursor): + PROXIES = ( + clang.cindex.Cursor, + [ + "storage_class", + ] + ) + CURSOR_KINDS = [CursorKind.VAR_DECL, CursorKind.FIELD_DECL] + + @property + def type(self): + return Type._wrapped(self.proxied_object.type) + + +class Type(clangcplus.Type): + def get_declaration(self): + return Cursor._wrapped(self.proxied_object.get_declaration()) + + @property + def is_a_function(self): + return isinstance(self, FunctionType) + + def decomposition(self): + prefixes, name, operators, suffixes = utils.decompose_type(self.proxied_object.spelling) + return prefixes, name, operators, suffixes, None + + +class ArrayType(Type): + PROXIES = ( + clang.cindex.Type, + [ + "element_type", + ] + ) + TYPE_KINDS = [TypeKind.CONSTANTARRAY, TypeKind.VARIABLEARRAY, TypeKind.INCOMPLETEARRAY] + + def __init__(self, array, element_count=None): + super(ArrayType, self).__init__(array) + if self.kind == TypeKind.CONSTANTARRAY: + self._element_count = self.proxied_object.element_count + elif self.kind == TypeKind.VARIABLEARRAY: + assert element_count is not None + self._element_count = element_count + else: + assert element_count is None + self._element_count = element_count + + @property + def element_count(self): + return self._element_count + + @property + def spelling(self): + decl = self.proxied_object.spelling + if "(anonymous " in decl: + # + # The spelling will be of the form '(anonymous struct at /usr/include/KF5/libkleo/oidmap.h:36:14) const[12]' + # + words = decl.split("(", 1)[1] + words = words.rsplit(")", 1) + decl = de_anonymiser(words[0]) + if len(words) > 1: + decl += words[1] + return decl + + @property + def underlying_type(self): + return self._wrapped(self.proxied_object.element_type) + + +class FunctionType(clangcplus.FunctionType, Type): + def fmt_result(self): + result = self.result_type.spelling + if result[-1] not in "*&": + result += " " + return result + + def fmt_args(self): + args = [c.spelling for c in self.argument_types] + if args: + return ", ".join(args) + else: + return "void" + + def fmt_name(self, name): + clazz = self.is_member_of + name = "{}::*{}".format(clazz.spelling, name) if clazz else name + if self.is_pointer: + name = "*" + name + return name + + +class UnexposedType(Type): + TYPE_KINDS = [TypeKind.UNEXPOSED] + + @property + def underlying_type(self): + return Type._wrapped(self.proxied_object.get_canonical()) + + +class ElaboratedType(UnexposedType): + TYPE_KINDS = [TypeKind.ELABORATED] + + def decomposition(self): + prefixes, name, operators, suffixes, underlying = self.underlying_type.decomposition() + return prefixes, name, operators, suffixes, underlying + + +class TypedefType(UnexposedType): + TYPE_KINDS = [TypeKind.TYPEDEF] + + def decomposition(self): + prefixes, name, operators, suffixes, underlying = super(TypedefType, self).decomposition() + name = utils.fqn(self.proxied_object.get_declaration()) + return prefixes, name, operators, suffixes, self.underlying_type + + +class PointerType(clangcplus.PointerType, Type): + def decomposition(self): + prefixes, name, operators, suffixes, underlying = super(PointerType, self).decomposition() + name = utils.fqn(self.underlying_type.proxied_object.get_declaration()) + return prefixes, name, operators, suffixes, self.underlying_type + + +class ByteType(Type): + TYPE_KINDS = [TypeKind.CHAR_S, TypeKind.CHAR_U, TypeKind.SCHAR, TypeKind.UCHAR] + + +class IntegerType(Type): + TYPE_KINDS = [TypeKind.BOOL, TypeKind.CHAR16, TypeKind.CHAR32, TypeKind.USHORT, TypeKind.UINT, TypeKind.ULONG, + TypeKind.ULONGLONG, TypeKind.UINT128, TypeKind.SHORT, TypeKind.INT, TypeKind.LONG, TypeKind.LONGLONG, + TypeKind.INT128, TypeKind.ENUM] + + +class FloatType(Type): + TYPE_KINDS = [TypeKind.FLOAT, TypeKind.DOUBLE, TypeKind.FLOAT128, TypeKind.LONGDOUBLE] + + +class RecordType(Type): + TYPE_KINDS = [TypeKind.RECORD] + + @property + def spelling(self): + decl = self.proxied_object.spelling + if "(anonymous " in decl: + # + # The spelling will be of the form 'N::n::(anonymous union at /usr/include/KF5/kjs/bytecode/opargs.h:66:5)' + # + words = decl.split("(", 1)[1][:-1] + decl = de_anonymiser(words) + return decl + + +def de_anonymiser(words): + """ + The words will be of the form 'anonymous union at /usr/include/KF5/kjs/bytecode/opargs.h:66:5'. + """ + words = re.split("[ :]", words) + kind = {"enum": EnumCursor, "struct": StructCursor, "union": UnionCursor}[words[1]] + decl = kind.SIP_TYPE_NAME + " __" + words[1] + words[-2] + return decl Index: find-modules/module_generation/clangcplus.py =================================================================== --- /dev/null +++ find-modules/module_generation/clangcplus.py @@ -0,0 +1,446 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +"""A Clang-C wrapper with stronger typing and a more OO API than Clang-C.""" +import gettext +import logging + +import clang.cindex + +logger = logging.getLogger(__name__) +gettext.install(__name__) + +# Keep PyCharm happy. +_ = _ +CursorKind = clang.cindex.CursorKind +TypeKind = clang.cindex.TypeKind + + +class MetaProxy(type): + def __new__(mcs, name, bases, dct): + """ + Install all proxy handlers listed as PROXIES in the class, and its base classes. + """ + # + # The function gives us eager bindng. + # + def named_property(name): + @property + def getter(self): + return getattr(self.proxied_object, name) + + return getter + + # + # The function gives us eager bindng. + # + def named_getter(name): + return lambda self: getattr(self.proxied_object, name)() + + result = super(MetaProxy, mcs).__new__(mcs, name, bases, dct) + # + # MRO ordering provides for subclasses to override base classes. + # + for o in result.mro(): + proxy_type, proxy_attributes = getattr(o, "PROXIES", (None, [])) + for proxy_attribute in proxy_attributes: + # + # If not present as an override in the subclass, implement the proxy. + # + if hasattr(result, proxy_attribute): + continue + target = getattr(proxy_type, proxy_attribute) + if isinstance(target, property): + setattr(result, proxy_attribute, named_property(proxy_attribute)) + else: + setattr(result, proxy_attribute, named_getter(proxy_attribute)) + return result + + +class _Proxy(object): + __metaclass__ = MetaProxy + + def __init__(self, proxied_object): + """ + Initialise with the default object to be proxied. + """ + self.proxied_object = proxied_object + + def _add_proxy(self, proxied_object, proxy_attribute): + """ + For each item in proxy_attributes, add a proxy from self to a proxied_object. + """ + # + # If not present as an override in the subclass, implement the proxy. + # + if not hasattr(self, proxy_attribute): + setattr(self, proxy_attribute, getattr(proxied_object, proxy_attribute)) + + +class Diagnostic(_Proxy): + """ + The same as a cindex.Diagnostic, but with Pythonic severity. + """ + PROXIES = ( + clang.cindex.Diagnostic, + # + # All attributes except severity. + # + [ + "category_name", "category_number", "children", "disable_option", "fixits", "location", "option", "ranges", "spelling" + ] + ) + CLANG_TO_LOGGING = (logging.NOTSET, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) + + def __init__(self, diagnostic): + super(Diagnostic, self).__init__(diagnostic) + + @property + def severity(self): + """ + The diagnostic levels in cindex.py are + + Ignored = 0 + Note = 1 + Warning = 2 + Error = 3 + Fatal = 4 + + and the levels in the python logging module are + + NOTSET 0 + DEBUG 10 + INFO 20 + WARNING 30 + ERROR 40 + CRITICAL 50 + + """ + return self.CLANG_TO_LOGGING[self.proxied_object.severity] + + +class MetaCursor(MetaProxy): + def __new__(mcs, name, bases, dct): + """ + Register the class against the cindex.CursorKinds it is a proxy for. + Custom parsers can be built by: + + 1. Provide a base class derived from Cursor with its own CLASS_MAP: + + class MyCursor(Cursor): + CLASS_MAP = {} + + 2. Override ALL the CLASS_MAP entries registered to the base + Cursor, such as TranslationUnitCursor, by adding MyCursor as a right-most + base class: + + class MyTranslationUnit(TranslationUnitCursor, MyCursor): + ''' + The MRO ensures we pick up MyCursor->CLASS_MAP (and not + TranslationUnitCursor->Cursor->CLASS_MAP). + ''' + pass + + 3. Adding any new wrappers by subclassing MyCursor and specifying + the CURSOR_KINDS it wraps, and any attributes to be proxied: + + class MyStruct(MyCursor): + CURSOR_KINDS = [CursorKind.STRUCT_DECL] + + def __init__(self, container): + proxy_attributes = ["item_to_proxy", ...] + super(MyStruct, self).__init__(container, proxy_attributes) + """ + result = super(MetaCursor, mcs).__new__(mcs, name, bases, dct) + # + # Register the class type against the kinds it proxies. + # + for kind in getattr(result, "CURSOR_KINDS"): + result.CLASS_MAP[kind] = result + return result + + +class Cursor(_Proxy): + """ + This is the base class for all objects we wrap (unwrapped objects are still + exposed as cindex.Cursor). + """ + __metaclass__ = MetaCursor + PROXIES = ( + clang.cindex.Cursor, + # + # All Cursors proxy some standard attributes for baseline compatibility with cindex.Cursor. + # + [ + "access_specifier", "displayname", "extent", "kind", "location", "referenced", "spelling", + ] + ) + CLASS_MAP = {} + CURSOR_KINDS = [] + + def get_children(self): + """ + Get the children of this Cursor either as Cursors, or as clang.cindex.Cursor. + """ + for child in self.proxied_object.get_children(): + yield self._wrapped(child) + + @property + def semantic_parent(self): + return self._wrapped(self.proxied_object.semantic_parent) + + @property + def translation_unit(self): + return self._wrapped(self.proxied_object.translation_unit.cursor) + + @classmethod + def _wrapped(cls, cursor): + """ + Wrap a cindex.cursor if possible. + + :param cursor: The cindex.Cursor to wrap. + :return: If possible, return the wrapped cindex_cursor, else just return the cindex_cursor. + """ + try: + # + # A CursorKind.TRANSLATION_UNIT is not a clang.cindex.Cursor, + # and as such is the only thing without a "kind". + # + kind = getattr(cursor, "kind", CursorKind.TRANSLATION_UNIT) + except ValueError as e: + # + # Some kinds result in a Clang error. + # + logger.debug(_("Unknown _kind_id {} for {}".format(e, cursor.spelling))) + else: + try: + clazz = cls.CLASS_MAP[kind] + except KeyError: + # + # Some kinds don't have wrappers. + # + pass + else: + cursor = clazz(cursor) + return cursor + + +class TranslationUnitCursor(Cursor): + """ + Surprise: a translation unit is also a Cursor! + """ + CURSOR_KINDS = [CursorKind.TRANSLATION_UNIT] + CHECKED = False + + def __init__(self, tu): + super(TranslationUnitCursor, self).__init__(tu) + # + # Check that the user fully overrode the CLASS_MAP. Doing this check + # here makes sense as this is normally the first Cursor instantiated. + # + if not TranslationUnitCursor.CHECKED: + for base_class in self.__class__.mro(): + base_class_map = getattr(base_class, "CLASS_MAP", self.CLASS_MAP) + if self.CLASS_MAP is not base_class_map: + tmp = [base_class_map[k] for k in base_class_map if k not in self.CLASS_MAP] + if tmp: + tmp = list(set(tmp)) + # + # The user forgot to override something. + # + raise AssertionError(_("CLASS_MAP items from {} not overridden by {}: {}").format( + base_class, self.__class__, tmp)) + TranslationUnitCursor.CHECKED = True + # + # In clang.cindex, a translation unit is not a cursor. So here, we need + # merge a couple of attributes "manually". + # + proxy_attributes = ["get_includes", "get_tokens", "source_processor"] + for attr in proxy_attributes: + self._add_proxy(tu.translation_unit, attr) + + @property + def diagnostics(self): + for d in self.proxied_object.translation_unit.diagnostics: + yield Diagnostic(d) + + @property + def translation_unit(self): + return self + + +class ContainerCursor(Cursor): + CURSOR_KINDS = [CursorKind.NAMESPACE, CursorKind.CLASS_DECL] + + +class MetaType(MetaProxy): + def __new__(mcs, name, bases, dct): + """ + Register the class against the cindex.TypeKinds it is a proxy for. + Custom parsers can be built by: + + 1. Provide a base class derived from Type with its own CLASS_MAP: + + class MyType(Type): + CLASS_MAP = {} + + 2. Override ALL the CLASS_MAP entries registered to the base + Type, such as TranslationUnitCursor, by adding MyType as a right-most + base class: + + class MyArray(Array, MyType): + ''' + The MRO ensures we pick up MyType->CLASS_MAP (and not + Array->Type->CLASS_MAP). + ''' + pass + + 3. Adding any new wrappers by subclassing MyType and specifying + the TYPE_KINDS it wraps, and any attributes to be proxied: + + class MyVector(MyType): + TYPE_KINDS = [TypeKind.VECTOR] + + def __init__(self, vector): + proxy_attributes = ["item_to_proxy", ...] + super(MyVector, self).__init__(vector, proxy_attributes) + """ + result = super(MetaType, mcs).__new__(mcs, name, bases, dct) + # + # Register the class type against the kinds it proxies. + # + for kind in getattr(result, "TYPE_KINDS"): + result.CLASS_MAP[kind] = result + return result + + +class Type(_Proxy): + """ + This is the base class for all objects we wrap (unwrapped objects are still + exposed as cindex.Type). + """ + __metaclass__ = MetaType + PROXIES = ( + clang.cindex.Type, + # + # All Types proxy some standard attributes for baseline compatibility with cindex.Type. + # + [ + "is_const_qualified", "kind", "spelling", + ] + ) + CLASS_MAP = {} + TYPE_KINDS = [""] + + @classmethod + def _wrapped(cls, type_): + """ + Wrap a cindex.type if possible. + + :param type_: The cindex.Type to wrap. + :return: If possible, return the wrapped cindex_type, else just return the cindex_type. + """ + try: + if type_.kind == TypeKind.POINTER and \ + type_.get_pointee().get_canonical().kind in FunctionType.TYPE_KINDS: + # + # FunctionCursor pointer case. + # + clazz = cls.CLASS_MAP[type_.get_pointee().get_canonical().kind] + else: + clazz = cls.CLASS_MAP[type_.kind] + except KeyError: + # + # Some kinds don't have wrappers. For them, in order to provide a + # wrapped version of get_canonical(), we act as the wrapper. + # + return cls.CLASS_MAP[""](type_) + else: + type_ = clazz(type_) + return type_ + + def get_canonical(self): + return self._wrapped(self.proxied_object.get_canonical()) + + +class FunctionType(Type): + """ + For a function or function pointer. + + NOTE: In the function pointer case, self.kind == TypeKind.POINTER. + """ + TYPE_KINDS = [TypeKind.MEMBERPOINTER, TypeKind.FUNCTIONPROTO] + + @property + def is_pointer(self): + """ + Is this a pointer-to-function? + """ + return self.kind == TypeKind.POINTER + + @property + def is_member_of(self): + """ + Return the class if this is a (pointer-to-)member function, or None. + """ + type_ = self.proxied_object.get_canonical() + if self.is_pointer: + type_ = type_.get_pointee().get_canonical() + if type_.kind == TypeKind.MEMBERPOINTER: + return type_.get_class_type() + return None + + @property + def argument_types(self): + type_ = self.proxied_object.get_canonical() + if self.is_pointer: + type_ = type_.get_pointee().get_canonical() + if type_.kind == TypeKind.MEMBERPOINTER: + type_ = type_.get_pointee().get_canonical() + return type_.argument_types() if type_.kind != TypeKind.POINTER else [] + + @property + def result_type(self): + type_ = self.proxied_object.get_canonical() + if self.is_pointer: + type_ = type_.get_pointee().get_canonical() + if type_.kind == TypeKind.MEMBERPOINTER: + type_ = type_.get_pointee().get_canonical() + return type_.get_result() if type_.kind != TypeKind.POINTER else type_ + + +class PointerType(Type): + """ + For a pointer which is not a function pointer. + + NOTE: In the pointer case, self.kind == TypeKind.POINTER, but see also FunctionType. + """ + TYPE_KINDS = [TypeKind.POINTER, TypeKind.LVALUEREFERENCE] + + @property + def underlying_type(self): + return self._wrapped(self.proxied_object.get_pointee()) Index: find-modules/module_generation/module_compiler.py =================================================================== --- /dev/null +++ find-modules/module_generation/module_compiler.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +"""Run SIP compiler and C++ compiler for a SIP binding module.""" +from __future__ import print_function +import argparse +import errno +import gettext +import glob +import os +import inspect +import logging +import multiprocessing +from multiprocessing.pool import Pool +import re +import shutil +import sipconfig +import subprocess +import sys +import traceback + +from PyQt5.QtCore import PYQT_CONFIGURATION + +from module_generator import INCLUDES_EXTRACT, MODULE_SIP, feature_for_sip_module, get_platform_dependencies +import rules_engine + + +class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): + pass + + +logger = logging.getLogger(__name__) +gettext.install(__name__) + +# Keep PyCharm happy. +_ = _ + + +FILE_SORT_KEY=str.lower + + +class ModuleCompiler(object): + def __init__(self, rules_pkg, verbose, input, output): + """ + Constructor. + + :param rules_pkg: The rules for the project. + :param verbose: Debug info. + :param input: The source SIP directory. + :param output: The Python and shippable SIP output directory. + """ + # + # Find the SIP compiler.. + # + lib_clang, exe_clang, sys_includes, exe_sip = get_platform_dependencies(output) + self.exe_sip = exe_sip + self.rules_pkg = rules_pkg + self.compiled_rules = rules_engine.rules(self.rules_pkg) + self.package = self.compiled_rules.sip_package() + self.compile_flags = self.compiled_rules.cxx_compile_flags() + self.imports = self.compiled_rules.sip_imports() + self.libraries = [] + self.libdirs = set() + for lg in self.compiled_rules.cxx_libraries(): + libs = [":" + os.path.basename(l) for l in glob.glob(lg)] + self.libraries.extend(libs) + libdirs = [os.path.dirname(l) for l in glob.glob(lg)] + self.libdirs.update(libdirs) + self.libdirs = [l for l in self.libdirs] + self.verbose = verbose + self.input_sips = input + self.tmp = os.path.join(output, "tmp") + self.output_so = os.path.join(output, "python") + # + # We are going to process a whole set of modules. We therefore assume + # we will need to fixup recursive %Imports and create a shippable + # package of .sip files and bindings. + # + self.output_sips = os.path.join(output, "sip") + + def process_tree(self, jobs, selector, omitter): + """ + Run a set of SIP files, but don't throw any errors. + + :param jobs: How many jobs to run in parallel? 0 for serial inline (debug) mode. + :param omitter: A regular expression which sets the files from project_root NOT to be processed. + :param selector: A regular expression which limits the files from project_root to be processed. + :return: (attempts, [failures]) + """ + # + # Set up the project output directory. + # + for dir in [self.tmp, self.output_so, self.output_sips]: + try: + os.makedirs(os.path.join(dir, self.package)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + with open(os.path.join(self.output_so, self.package, "__init__.py"), "w") as f: + pass + # + # If there is a set of features, copy it. + # + features = "modules.features" + try: + copy_file(self.input_sips, features, os.path.join(self.output_sips, self.package)) + except: + features = None + per_process_args = [] + for dirpath, dirnames, filenames in os.walk(self.input_sips): + # + # Use sorted walks. + # + dirnames.sort(key=FILE_SORT_KEY) + filenames.sort(key=FILE_SORT_KEY) + for filename in filenames: + if filename != os.path.basename(dirpath) + MODULE_SIP and filename != self.package + MODULE_SIP: + continue + sip_file = os.path.join(dirpath, filename) + sip_file = sip_file[len(self.input_sips) + len(os.path.sep):] + # + # Was this file selected? + # + if not(selector.search(sip_file) and not omitter.search(sip_file)): + continue + per_process_args.append((sip_file, )) + std_args = (self.exe_sip, self.input_sips, self.imports, self.libraries, self.libdirs, self.compile_flags, + self.package, self.output_so, self.output_sips, self.tmp, features, self.verbose) + if jobs <= 1: + # + # Debug mode. + # + results = [process_one(*(process_args + std_args)) for process_args in per_process_args] + else: + # + # Parallel processing here... + # + tp = Pool(processes=jobs) + results = [tp.apply_async(process_one, process_args + std_args) for process_args in per_process_args] + tp.close() + tp.join() + # + # Serial again. + # + results = [r.get() for r in results] + results = {result[0]: result[1:] for result in results} + failures = [] + for sip_file in sorted(results.keys(), key=FILE_SORT_KEY): + e = results[sip_file][0] + if e: + failures.append((sip_file, e)) + return len(results), failures + + +def copy_file(input_dir, filename, output_dir): + output = os.path.join(output_dir, filename) + try: + os.makedirs(os.path.dirname(output)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + input = os.path.join(input_dir, filename) + shutil.copy(input, output) + + +def process_one(sip_file, exe_sip, input_sips, imports, libraries, libdirs, compile_flags, package, output_so, + output_sips, tmp_dir, features, verbose): + """ + Run a SIP file. + + :param sip_file: A SIP file name. + :param exe_sip: The SIP compiler. + :param input_sips: Config + :param imports: Config + :param libraries: Config + :param compile_flags: The compile flags for the file. + :param package: Config + :param output_so: Config + :param output_sips: Config + :param tmp_dir: Config + :param features: Is there a features file? + :param verbose: Turn on diagnostics for command lines. + :return: ( + sip_file, + error, + ) + """ + # + # Make sure any errors mention the file that was being processed. + # + try: + # + # Get the SIP configuration information. + # + self_sipconfig = sipconfig.Configuration() + pyqt_sip_flags = PYQT_CONFIGURATION["sip_flags"].split() + # + # Read the supplied SIP module for the module name and the included modules. + # + feature = feature_for_sip_module(sip_file) + match_re = re.compile("%Module(\s+|\s*\(\s*name\s*=\s*)(?P[^\s,)]+).*") + include_re = re.compile("%Include(\s+|\s*\(\s*name\s*=\s*)(?P[^\s,)]+).*") + module_name = None + included_modules = [] + with open(os.path.join(input_sips, sip_file), "rU") as i: + for line in i: + m = match_re.match(line) + if m: + module_name = m.group("name") + else: + m = include_re.match(line) + if m: + assert module_name, _("%Include not preceeded by %Module") + included_modules.append(m.group("name")) + assert module_name, _("No %Module found") + # + # Set up the module output directory. + # + module_path = module_name.replace(".", os.path.sep) + tmp_dir = os.path.join(tmp_dir, module_path) + output_so = os.path.join(output_so, os.path.dirname(module_path)) + assert module_name.startswith(package + ".") or module_name == package, \ + _("Expected {} to be part of {}").format(module_name, package) + output_sips = os.path.join(output_sips, package) + for dir in [tmp_dir, output_so, output_sips]: + try: + os.makedirs(dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + # + # Create the set of output SIP files: + # + # - The original SIP module. + # - Any %Included SIP modules. + # - Optionally, write a version of the supplied SIP module with feature support included. + # + copy_file(input_sips, sip_file, output_sips) + for i in included_modules: + copy_file(input_sips, i, output_sips) + if features: + source = os.path.join(output_sips, os.path.splitext(sip_file)[0] + ".with_features.sip") + with open(source, "w") as o: + # + # Suppress the feature that corresponds to the SIP file being processed to avoid feeding SIP + # %Import clauses which recursively refer to module beng processed. we do this by cloaking each + # in a %Feature, and then disabling the one for "this". + # + # To avoid defining the %Feature multiple time, we put them inline in the temporary module. + # + o.write("// Run SIP with '-x {}' to prevent recursive definitions.\n".format(feature)) + o.write("%Include(name={})\n".format(features)) + o.write("%Include(name={})\n".format(os.path.basename(sip_file))) + else: + source = os.path.join(output_sips, sip_file) + # + # Run the SIP compiler. Use the -X option to extract all include paths actually used by the compiler. + # + build_file = os.path.join(tmp_dir, "module.sbf") + make_file = os.path.join(tmp_dir, "module.Makefile") + module_includes = os.path.join(tmp_dir, "module.includes") + logger.info(_("Compiling {}").format(source)) + cmd = [exe_sip, "-c", tmp_dir, "-b", build_file, "-e", "-o", "-w", "-X", + INCLUDES_EXTRACT + ":" + module_includes] + if features: + cmd += ["-x", feature] + cmd += pyqt_sip_flags + ["-I" + i for i in imports + [input_sips]] + [source] + for cpp in glob.iglob(os.path.join(tmp_dir, "*.cpp")): + os.unlink(cpp) + _run_command(verbose, cmd) + # + # Create the Makefile. + # + includes = open(module_includes, "rU").read().split("\n") + includes = [i for i in includes if i] + self_sipconfig._macros["INCDIR"] = " ".join(includes) + makefile = sipconfig.SIPModuleMakefile(self_sipconfig, build_file, makefile=make_file) + makefile.extra_cxxflags = compile_flags + # + # Link against the user-specified libraries. Typically, any one module won't need them all, but this + # is better than having to specify them by hand. + # + makefile.extra_libs = libraries + makefile.extra_lib_dirs = libdirs + makefile.generate() + # + # It is much faster to compile once only, so combine all the .cpp files into one. + # + unified = "unified" + os.path.basename(module_path) + with open(os.path.join(tmp_dir, unified + ".cpp_"), "w") as f: + for cpp in glob.iglob(os.path.join(tmp_dir, "*.cpp")): + f.write('#include "{}"\n'.format(os.path.basename(cpp))) + os.rename(f.name, os.path.join(tmp_dir, unified + ".cpp")) + # + # Compile and publish the module. + # TODO: The hardcoded ".so" is not portable. + # + _run_command(verbose, ["make", "-f", os.path.basename(make_file), "OFILES=" + unified + ".o"], cwd=tmp_dir) + cpython_module = os.path.basename(module_path) + ".so" + logger.info(_("Publishing {}").format(module_name)) + copy_file(tmp_dir, cpython_module, output_so) + with open(os.path.join(output_so, "__init__.py"), "w") as f: + pass + except Exception as e: + logger.error("{} while processing {}".format(e, sip_file)) + # + # Tracebacks cannot be pickled for use by multiprocessing. + # + e = (e.__class__, str(e), traceback.format_exc()) + return sip_file, e + return sip_file, None + + +def _run_command(verbose, cmd, *args, **kwds): + if verbose: + logger.info(" ".join(cmd)) + sub = subprocess.Popen(cmd, *args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwds) + stdout, stderr = sub.communicate() + stdout = stdout.strip() + if sub.returncode: + raise RuntimeError(stdout) + if verbose and stdout: + print(stdout) + + +def main(argv=None): + """ + Run the SIP compiler, and the "make" the generated code. By default, all + the modules specified in the rules file will be processed. The set of files + can be restricted using the --select option, or a single standalone file + specified using the "@" prefix to the --select option. + + The standalone mode also bypasses all of the special processing associated + with the bulk processing of modules (see README) because it is assumed the + user knows best. + + Examples: + + module_compiler.py --select KDBusAddons sip output + module_compiler.py sip output + """ + if argv is None: + argv = sys.argv + parser = argparse.ArgumentParser(epilog=inspect.getdoc(main), + formatter_class=HelpFormatter) + parser.add_argument("-v", "--verbose", action="store_true", default=False, help=_("Enable verbose output")) + parser.add_argument("--select", default=".*", type=lambda s: re.compile(s, re.I), + help=_("Regular expression of SIP modules under 'input' to be processed")) + parser.add_argument("--omit", default="", type=lambda s: re.compile(s, re.I), + help=_("Regular expression of C++ headers under 'input' NOT to be processed")) + parser.add_argument("-j", "--jobs", type=int, default=multiprocessing.cpu_count(), + help=_("Number of parallel jobs, 0 for serial inline operation")) + parser.add_argument("rules", help=_("Project rules package")) + parser.add_argument("input", help=_("SIP input directory to process")) + parser.add_argument("output", help=_("Python and shippable SIP output directory")) + try: + args = parser.parse_args(argv[1:]) + if args.verbose: + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s: %(message)s') + else: + logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') + # + # Compile! + # + input = os.path.normpath(args.input) + output = os.path.normpath(args.output) + rules_pkg = os.path.normpath(args.rules) + d = ModuleCompiler(rules_pkg, args.verbose, input, output) + attempts, failures = d.process_tree(args.jobs, args.select, args.omit) + # + # Dump a summary of what we did. Order the results by the name of the source. + # + for sip_file, e in failures: + logger.info(_("Summary: {}: {} while processing {}").format(e[0].__name__, e[1], sip_file)) + level = logging.ERROR if failures else logging.INFO + logger.log(level, _("Summary: {} processing errors for {} modules").format(len(failures), attempts)) + if failures: + tbk = failures[0][1][2] + print(tbk) + return -1 + except Exception as e: + tbk = traceback.format_exc() + print(tbk) + return -1 + + +if __name__ == "__main__": + sys.exit(main()) Index: find-modules/module_generation/module_generator.py =================================================================== --- /dev/null +++ find-modules/module_generation/module_generator.py @@ -0,0 +1,934 @@ +#!/usr/bin/env python +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +"""Run SIP generator across a set of .h files, and create the SIP binding module.""" +from __future__ import print_function +import argparse +import datetime +import errno +import gettext +import glob +import os +import inspect +import logging +import multiprocessing +from multiprocessing.pool import Pool, ThreadPool +import re +import sys +import traceback + +from clang import cindex + +import rules_engine +from sip_generator import SipGenerator + + +class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): + pass + + +logger = logging.getLogger(__name__) +gettext.install(__name__) + +# Keep PyCharm happy. +_ = _ + + +MODULE_SIP = "mod.sip" +INCLUDES_EXTRACT = "includes" +FILE_SORT_KEY = str.lower + + +def feature_for_sip_module(sip_file): + """Convert a SIP module name into a feature name.""" + return os.path.splitext(sip_file)[0].replace(os.path.sep, "_").replace(".", "_").replace("+", "_") + + +class RuleUsage(dict): + """ + When using multiprocessing, we need some way to gather the rule usage counts. This class provides the logic for + accumulating the stats from remote as well as the local process. + """ + def __init__(self): + super(RuleUsage, self).__init__() + + def add_local_stats(self, rule_set): + """ + Statistics from local usage where we have access to the rule database directly. + + :param rule_set: + :return: + """ + def accumulate_local(rule, usage_count): + self.setdefault(str(rule), 0) + self[rule] += usage_count + + rule_set.dump_unused(accumulate_local) + + def add_remote_stats(self, stats): + """ + Statistics from remote processes where we do not have access to the + rule database directly (not least because rule databases contain + regular expression which cannot be pickled). + """ + for rule, usage_count in stats.items(): + self.setdefault(rule, 0) + self[rule] += usage_count + + +class IncludeToImportMap(dict): + def __init__(self, project_root): + """ + Initialise the dictionary with mappings based on the project root. + + :param root: The root we started with. + :param parent: The current recursion point. + """ + super(IncludeToImportMap, self).__init__() + self.fill_from_includes(project_root, project_root) + # + # These are the SIP files that belong to the project. + # + self.predicted_sips = list(self.values()) + + def __getitem__(self, item): + return super(IncludeToImportMap, self).__getitem__(item.lower()) + + def __setitem__(self, key, value): + super(IncludeToImportMap, self).__setitem__(key.lower(), value) + + def fill_from_includes(self, root, parent): + """ + Recursively fill the dictionary with mappings based on root. + + :param root: The root we started with. + :param parent: The current recursion point. + """ + names = ModuleGenerator.included_dir_names(os.listdir(parent)) + for name in names: + srcname = os.path.join(parent, name) + if os.path.isdir(srcname): + # + # Any C++ definitions picked up by #include from this dirname + # should match SIP definitions from this module. + # + module_dir = srcname[len(root) + len(os.path.sep):] + sip = os.path.join(module_dir, name + MODULE_SIP) + for funny in ModuleGenerator.FUNNY_CHARS: + sip = sip.replace(funny, "_") + self[module_dir] = sip + self.fill_from_includes(root, srcname) + + +class ModuleGenerator(object): + def __init__(self, rules_pkg, output_dir, dump_modules=False, dump_items=False, dump_includes=False, + dump_privates=False, verbose=False): + """ + Constructor. + + :param rules_pkg: The rules for the project. + :param output_dir: The destination SIP directory. + :param dump_modules: Turn on tracing for modules. + :param dump_items: Turn on tracing for container members. + :param dump_includes: Turn on diagnostics for include files. + :param dump_privates: Turn on diagnostics for omitted private items. + :param verbose: Turn on diagnostics for command lines. + """ + super(ModuleGenerator, self).__init__() + # + # Find and load the libclang. + # + lib_clang, exe_clang, sys_includes, exe_sip = get_platform_dependencies(output_dir) + cindex.Config.set_library_file(lib_clang) + self.exe_clang = exe_clang + # + # Get paths. + # + self.rules_pkg = rules_pkg + self.compiled_rules = rules_engine.rules(self.rules_pkg) + self.project_root = self.compiled_rules.cxx_source_root() + self.package = self.compiled_rules.sip_package() + self.includes = self.compiled_rules.cxx_includes() + self.compile_flags = self.compiled_rules.cxx_compile_flags() + ["-isystem" + i for i in sys_includes] + self.exploded_includes = list(self.includes) + for i in self.includes: + for dirpath, dirnames, filenames in os.walk(i): + for d in dirnames: + d = os.path.join(dirpath, d) + if d not in self.exploded_includes: + self.exploded_includes.append(d) + self.imports = self.compiled_rules.sip_imports() + self.output_dir = output_dir + self.dump_modules = dump_modules + self.dump_items = dump_items + self.dump_includes = dump_includes + self.dump_privates = dump_privates + self.verbose = verbose + # + # One of the problems we want to solve is that for each #include in the transitive fanout for a .h under + # self.project_root, we need to insert a %Import for the corresponding module-level .sip. To do this, we assume + # that for the relative path aaa/bbb/ccc/foo/bar.h, the needed module is at aaa/bbb/ccc/foo/foomod.sip. + # + # However, this is made tricky because we want to use new-style CamelCase names whereever possible: + # + # - Under self.imports, we simply walk the directory structure, folding into any non-lower-case-only names + # when possible, and add each xxxmod.sip we find to the available set. + # + # - Under self.output_dir, of course, the .sip files don't actually exist before we start running. So here, + # we walk self.project_root, folding into any non-lower-case-only names when possible, and add the + # xxxxmod.sip name that will be created to the available set. + # + self.include_to_import_cache = IncludeToImportMap(self.project_root) + for sip_root in self.imports: + self.include_to_import_cache.fill_from_includes(sip_root, sip_root) + self.all_features = None + self.rule_usage = RuleUsage() + self.omitter = None + self.selector = None + self.lock = multiprocessing.Lock() + + @staticmethod + def included_dir_names(names): + """ + Use de-duped and sorted walks. + """ + # + # Eliminate the duplication of forwarding directories. + # + new_style_names = [n for n in names if n != n.lower() and n.lower() in names] + for name in new_style_names: + names.remove(name.lower()) + names.sort(key=FILE_SORT_KEY) + return names + + def included_h_names(self, dirpath, names): + """ + Use de-duped, filtered and sorted walks. + """ + # + # Eliminate the duplication of forwarding headers. + # + new_style_names = [n for n in names if not n.endswith(".h") and n.lower() + ".h" in names] + for name in new_style_names: + names.remove(name.lower() + ".h") + # + # Eliminate non-selected and omitted headers. + # + dirpath = dirpath[len(self.project_root) + len(os.path.sep):] + tmp_names = [] + for n in names: + h_file = os.path.join(dirpath, n) + if self.compiled_rules.cxx_selector.search(h_file) and not self.compiled_rules.cxx_omitter.search(h_file): + if self.selector.search(h_file) and not self.omitter.search(h_file): + tmp_names.append(n) + tmp_names.sort(key=FILE_SORT_KEY) + return tmp_names + + def process_tree(self, jobs, selector, omitter): + """ + Run a set of SIP modules, but don't throw any errors. + + :param jobs: How many jobs to run in parallel? 0 for serial inline (debug) mode. + :param selector: A regular expression which limits the files from project_root to be processed. + :param omitter: A regular expression which sets the files from project_root NOT to be processed. + :return: (attempts, [failures]) + """ + self.omitter = omitter + self.selector = selector + self.all_features = set() + attempts = 0 + failures = [] + directories = 0 + sources = self.compiled_rules.cxx_sources() + if sources: + sources = [s[len(self.project_root):] for s in sources] + sources = self.included_dir_names(sources) + sources = [self.project_root + s for s in sources] + else: + sources = [self.project_root] + per_thread_args = [] + for source in sources: + if os.path.isdir(source): + for dirpath, dirnames, filenames in os.walk(source): + dirnames = self.included_dir_names(dirnames) + filenames = self.included_h_names(dirpath, filenames) + if filenames: + per_thread_args.append((dirpath, filenames)) + else: + # + # Assume it is a single-directory re. + # + dirpath = os.path.dirname(source) + filename_re = re.compile(os.path.basename(source)) + matcher = lambda dirpath, f: os.path.isfile(os.path.join(dirpath, f)) and filename_re.match(f) + filenames = [f for f in os.listdir(dirpath) if matcher(dirpath, f)] + filenames = self.included_h_names(dirpath, filenames) + if filenames: + per_thread_args.append((dirpath, filenames)) + # + # When we have plenty of cores available, we can end up wasting + # multiprocessing opportunities when there are not enough .h + # files in a directory. So try a bit of threading here. + # + max_h_files = 4 + thread_jobs = min(jobs // max_h_files, len(per_thread_args)) + process_jobs = jobs // (thread_jobs or 1) + logger.info(_("Using {} threads with {} processes each").format(thread_jobs, process_jobs)) + std_args = (process_jobs, ) + if thread_jobs <= 1: + # + # Debug mode. + # + results = [self.create_package_safe(*(std_args + thread_args)) for thread_args in per_thread_args] + else: + # + # Parallel processing here... + # + tp = ThreadPool(processes=thread_jobs) + results = [tp.apply_async(self.create_package_safe, std_args + thread_args) for thread_args in + per_thread_args] + tp.close() + tp.join() + # + # Serial again. Order the results by the dirpath. + # + results = [r.get() for r in results] + results = {result[0]: result[1:] for result in results} + for dirpath in sorted(results.keys(), key=FILE_SORT_KEY): + a, f, e = tuple(results[dirpath]) + # + # Update the global collections. + # + attempts += a + failures += f + if a: + directories += 1 + if e: + failures.append((dirpath, e)) + feature_list = os.path.join(self.output_dir, "modules.features") + # + # TODO, make sure the entries are unique. + # + with open(feature_list, "w") as f: + for feature in sorted(self.all_features, key=FILE_SORT_KEY): + f.write("%Feature(name={})\n".format(feature)) + self.rule_usage.add_local_stats(self.compiled_rules) + return attempts, failures, directories + + FUNNY_CHARS = "+" + + def create_package_safe(self, process_jobs, dirname, filenames): + """ + Safely call create_package(). + + This function is capable of being run using threading to take advantage + of multiple cores (via multiprocessing and create_sip_safe()). + + As part of this, the programming contract is that no exceptions are to + be thrown; they are instead returned to the caller to take appropriate + action "synchronously". + """ + # + # Make sure any errors mention the directory that was being processed. + # + try: + attempts, failures = self.create_package(process_jobs, dirname, filenames) + except Exception as e: + logger.error("{} while processing {}".format(e, dirname)) + # + # Use the the same model as create_sip_safe(). + # + e = (e.__class__, str(e), traceback.format_exc()) + return dirname, 0, [], e + return dirname, attempts, failures, None + + def create_package(self, process_jobs, dirname, filenames): + """ + For a (sub)set of .h files in one directory, generate the individual SIP + files, plus a module-level SIP file. + """ + attempts = 0 + failures = [] + per_process_args = [] + for n in filenames: + source = os.path.join(dirname, n) + h_file = source[len(self.project_root) + len(os.path.sep):] + per_process_args.append((source, h_file)) + std_args = (self.exe_clang, self.project_root, self.rules_pkg, self.package, self.compile_flags, + self.exploded_includes, self.includes, self.output_dir, self.dump_modules, self.dump_items, + self.dump_includes, self.dump_privates, self.verbose) + if process_jobs <= 1: + # + # Debug mode. + # + results = [create_sip_safe(*(process_args + std_args)) for process_args in per_process_args] + else: + # + # Parallel processing here... + # + tp = Pool(processes=process_jobs) + results = [tp.apply_async(create_sip_safe, process_args + std_args) for process_args in per_process_args] + tp.close() + tp.join() + # + # Serial again. Order the results by the name of the source. + # + results = [r.get() for r in results] + results = {result[0]: result[1:] for result in results} + import_sips = set() + modulecode = {} + all_include_roots = set() + sip_files = [] + for source in sorted(results.keys(), key=FILE_SORT_KEY): + sip_file, tmp, direct_includes, i_paths, rule_usage, e = tuple(results[source]) + # + # Update the global collections. + # + attempts += 1 + if e: + failures.append((source, e)) + with self.lock: + self.rule_usage.add_remote_stats(rule_usage) + if sip_file: + modulecode.update(tmp) + sip_files.append(sip_file) + # + # Create something which the SIP compiler can process that includes what appears to be the + # immediate fanout from this module. + # + all_include_roots.update(i_paths) + # + # For each include, add the corresponding SIP module to the set to be %Import'd. + # + for include in direct_includes: + if self.compiled_rules.cxx_omitter.search(include) or self.omitter.search(include): + continue + if include.endswith(("_export.h", "_version.h")): + continue + sip = self._map_include_to_import(include) + if sip: + import_sips.add(sip) + else: + logger.warn(_("Cannot find SIP for {}").format(include)) + # + # Create a SIP module including all the SIP files in this directory. + # + # NOTE: this is really only best-effort; the output here might have to be edited, or indeed + # module files may need to be created from scratch if the logic here is not good enough. + # + if sip_files: + h_dir = dirname[len(self.project_root) + len(os.path.sep):] + if h_dir: + # + # Remove funny characters: the results must be Python-valid names. + # + for funny in ModuleGenerator.FUNNY_CHARS: + h_dir = h_dir.replace(funny, "_") + module = h_dir.replace(os.path.sep, ".") + output_file = os.path.join(h_dir, os.path.basename(h_dir) + MODULE_SIP) + else: + # + # Header files at the top level... + # + module = self.package + output_file = os.path.join(h_dir, self.package + MODULE_SIP) + # + # Write the header and the body. + # + full_output = os.path.join(self.output_dir, output_file) + try: + os.makedirs(os.path.dirname(full_output)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + if h_dir: + decl = "%Module(name={}.{})\n".format(self.package, module) + else: + decl = "%Module(name={})\n".format(module) + decl += """ +%ModuleHeaderCode +#pragma GCC visibility push(default) +%End +""" + feature = feature_for_sip_module(output_file) + with self.lock: + self.all_features.add(feature) + # + # Create something which the SIP compiler can process that includes what appears to be the + # immediate fanout from this module. + # + for sip_import in sorted(import_sips, key=FILE_SORT_KEY): + # + # Protect each %Import with a feature. The point of this is that we often see module A and B + # which need to %Import each other; this confuses the SIP compiler which does not like the + # %Import A encountered in B while processing A's %Import of B. However, we cannot simply + # declare the corresponding %Feature here because then we will end up with the same %Feature in + # both A and B which SIP also does not like. + # + if sip_import in self.include_to_import_cache.predicted_sips: + if sip_import != output_file: + feature = feature_for_sip_module(sip_import) + with self.lock: + self.all_features.add(feature) + decl += "%If ({})\n".format(feature) + decl += "%Import(name={})\n".format(sip_import) + decl += "%End\n" + else: + decl += "%Import(name={})\n".format(sip_import) + # + # Add all include paths actually used by the compiler. + # + decl += "%Extract(id={})\n".format(INCLUDES_EXTRACT) + for include in sorted(all_include_roots, key=FILE_SORT_KEY): + decl += "{}\n".format(include) + decl += "%End\n" + # + # Add all peer .sip files. + # + peers = "" + for sip_file in sip_files: + peers += "%Include(name={})\n".format(sip_file) + # + # Any module-related manual code (%ExportedHeaderCode, %ModuleCode, %ModuleHeaderCode or other + # module-level directives? + # + sip = { + "name": module, + "decl": decl, + "modulecode": modulecode, + "peers": peers, + } + body = "" + if self.dump_modules: + logger.info(_("Processing module for {}").format(output_file)) + with self.lock: + modifying_rule = self.compiled_rules.modulecode(output_file, sip) + if modifying_rule: + body += "// Modified {} (by {}):\n".format(output_file, modifying_rule) + logger.info(_("Creating {}").format(full_output)) + with open(full_output, "w") as f: + f.write(header(output_file, h_dir, self.package)) + f.write(body) + f.write(sip["decl"]) + # + # The modulecode dictionary ensures there can be no duplicates, even if multiple sip files might have + # contributed the same item. By emitting it here, it can provide declare-before-use (needed for + # %Exceptions). + # + # Also, since SIP cannot cope with the same %MappedType in different modules, the rules can be + # used to eliminate the duplicates. + # + f.write("\n") + for mc in sorted(sip["modulecode"]): + f.write(modulecode[mc]) + f.write("\n") + f.write(sip["peers"]) + f.write(sip["code"]) + return attempts, failures + + def _map_include_to_import(self, include): + """ + For a given include file, return the corresponding SIP module. + + :param include: The name of a header file. + :return: The name of a SIP module which represents the header file. + """ + for include_root in self.includes: + # + # Assume only EXACTLY one root matches. + # + if include.startswith(include_root): + tmp = include[len(include_root) + len(os.path.sep):] + try: + return self.include_to_import_cache[os.path.dirname(tmp)] + except KeyError: + break + return None + + +def create_sip_safe(h_file, h_suffix, exe_clang, h_root, rules_pkg, package, compile_flags, exploded_includes, + i_paths, output_dir, dump_modules, dump_items, dump_includes, dump_privates, verbose): + """ + Safely call create_sip(). + + This function is capable of being run using multiprocessing to take + advantage of multiple cores. + + As part of this, the programming contract is that no exceptions are to + be thrown; they are instead returned to the caller to take appropriate + action "synchronously". + + :return: ( + source, + ...items from create_sip()... + error, + ) + """ + # + # Make sure any errors mention the file that was being processed. + # + try: + sip_suffix, modulecode, direct_includes, i_paths, rule_usage = create_sip(h_file, h_suffix, exe_clang, h_root, + rules_pkg, package, compile_flags, + exploded_includes, i_paths, + output_dir, dump_modules, dump_items, + dump_includes, dump_privates, verbose) + except Exception as e: + logger.error("{} while processing {}".format(e, h_file)) + # + # Tracebacks cannot be pickled for use by multiprocessing. + # + e = (e.__class__, str(e), traceback.format_exc()) + return h_file, None, {}, [], [], {}, e + return h_file, sip_suffix, modulecode, direct_includes, i_paths, rule_usage, None + + +def create_sip(h_file, h_suffix, exe_clang, h_root, rules_pkg, package, compile_flags, exploded_includes, i_paths, + output_dir, dump_modules, dump_items, dump_includes, dump_privates, verbose): + """ + Generate an individual SIP file for a .h file. + + :param h_file: Source to be processed. + :param h_suffix: Source to be processed, right hand side of name. + :param exe_clang: The Clang compiler. + :param h_root: Config + :param rules_pkg: Config + :param package: Config + :param compile_flags: Config + :param exploded_includes: The exploded version of i_paths. + :param i_paths: Config + :param output_dir: Config + :param dump_modules: Turn on tracing for modules. + :param dump_items: Turn on tracing for container members. + :param dump_includes: Turn on diagnostics for include files. + :param dump_privates: Turn on diagnostics for omitted private items. + :param verbose: Turn on diagnostics for command lines. + :return: ( + sip_suffix, + dict(modulecode), + [direct includes from h_file], + [-I paths needed by h_file], + dict(rule_usage), + ) + """ + sip_suffix = None + all_includes = lambda: [] + direct_includes = [] + result = "" + modulecode = {} + rule_usage = {} + # + # Now, the set of exploded includes is project-wide, but some #includes + # might be order-sensitive, so order anything that "belongs" to the tree + # of the current file ahead of anything else. + # + tmp = [i for i in exploded_includes if h_file.lower().startswith(i.lower())] + tmp.sort() + tmp.reverse() + tmp += exploded_includes + compile_flags += ["-I" + i for i in tmp] + generator = SipGenerator(exe_clang, rules_pkg, compile_flags, dump_modules=dump_modules, dump_items=dump_items, + dump_includes=dump_includes, dump_privates=dump_privates, verbose=verbose) + if h_suffix.endswith("_export.h"): + pass + elif h_suffix.endswith("_version.h"): + pass + # + # It turns out that generating a SIP file is the wrong thing for version files. TODO: create + # a .py file directly. + # + if False: + version_defines = re.compile("^#define\s+(?P\S+_VERSION\S*)\s+(?P.+)") + with open(h_file, "rU") as f: + for line in f: + match = version_defines.match(line) + if match: + result += "{} = {}\n".format(match.group("name"), match.group("value")) + else: + result, modulecode, all_includes = generator.create_sip(h_file, h_suffix) + direct_includes = [i.include.name for i in all_includes() if i.depth == 1] + if result: + pass + elif len(direct_includes) == 1: + # + # A non-empty SIP file could not be created from the header file. That would be fine except that a + # common pattern is to use a single #include to create a "forwarding header" to map a legacy header + # (usually lower case, and ending in .h) into a CamelCase header. Handle the forwarding case... + # + if direct_includes[0].startswith(h_root): + # + # We could just %Include the other file, but that would ignore the issues that: + # + # - On filesystems without case sensitive semantics (NTFS) the two filenames usually only + # differ in case; actually expanding inline avoids making this problem worse (even if it + # is not a full solution). + # - The forwarding SIP's .so binding needs the legacy SIP's .so on the system, doubling the + # number of libraries (and adding to overall confusion, and the case-sensitivity issue). + # + result, modulecode, all_includes = generator.create_sip(direct_includes[0], h_suffix) + direct_includes = [i.include.name for i in all_includes() if i.depth == 1] + # + # From the set of includes, we want two things: + # + # 1. Infer the %Import'd items this SIP file depends on. We get this from the directly included files. + # + # 2. Infer the set of -I paths needed to compile the SIP compiler output. We get this from all + # included files (trimmed to omit ones from paths we did not explicity add to get rid of compiler-added + # files and the like). + # + # First the %Import... + # + tmp = set() + for include in direct_includes: + # + # Deal with oddities such as a/b/c/../x, or repeated separators. + # + include = os.path.normpath(include) + for i_path in i_paths: + if include.startswith(i_path): + tmp.add(include) + break + direct_includes = list(tmp) + # + # Now the -I...starting with the current file, construct a directory of sets keyed + # by all possible include paths. + # + tmp = {h_root: set()} + tmp[h_root].add(os.path.dirname(h_suffix)) + for i_path in i_paths: + tmp.setdefault(i_path, set()) + for include in all_includes(): + # + # Deal with oddities such as a/b/c/../x, or repeated separators. Then, under the + # matching include path, add the suffix of the actual include file. + # + include = include.include.name + include = os.path.normpath(include) + for i_path in tmp: + if include.startswith(i_path): + trimmed_include = include[len(i_path) + len(os.path.sep):] + trimmed_include = os.path.dirname(trimmed_include) + tmp[i_path].add(trimmed_include) + break + # + # Now, construct a new version of i_paths that has *everything*. + # + i_paths = set() + for i_path, trimmed_includes in tmp.items(): + i_paths.add(i_path) + for trimmed_include in trimmed_includes: + while trimmed_include: + # + # Without reading the source code, we don't know exactly how long the -I had to be, so we add + # all possible lengths. + # + possible_i_path = os.path.join(i_path, trimmed_include) + i_paths.add(possible_i_path) + trimmed_include = os.path.dirname(trimmed_include) + i_paths = list(i_paths) + if result: + # + # Remove funny characters: the results must be Python-valid names. + # + module_file = h_suffix + for funny in ModuleGenerator.FUNNY_CHARS: + module_file = module_file.replace(funny, "_") + # + # Generate a file header. We can always use a .sip suffix because the caller took care of any possible + # clash of forwarding header with a legacy header on filesystems with case-insensitive lookups (NTFS). + # + sip_basename = os.path.basename(module_file) + sip_basename = os.path.splitext(sip_basename)[0] + ".sip" + module_path = os.path.dirname(module_file) + # + # The SIP compiler ges very confused if you have a filename that matches a search path. Decollide... + # + if sip_basename == os.path.basename(module_path): + sip_basename += "_" + sip_suffix = os.path.join(module_path, sip_basename) + header_text = header(sip_suffix, h_suffix, package) + # + # Write the header and the body. + # + sip_file = os.path.join(output_dir, sip_suffix) + try: + os.makedirs(os.path.dirname(sip_file)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + logger.info(_("Creating {}").format(sip_file)) + with open(sip_file, "w") as f: + f.write(header_text) + f.write(result) + + def add_used(rule, usage_count): + """ + Fill the dict of the used rules. + """ + if usage_count > 0: + rule_usage[str(rule)] = usage_count + + generator.compiled_rules.dump_unused(add_used) + else: + logger.info(_("Not creating empty SIP for {}").format(h_file)) + return sip_suffix, modulecode, direct_includes, i_paths, rule_usage + + +def header(output_file, h_file, package): + """ + Override this to get your own preferred file header. + + :param output_file: The name of the output file. + :param h_file: The name of the input file. + :param package: + :return: + """ + template = """// +// This file, {}, is part of {}. +// It was derived from {}. +// +%Copying +// Copyright (c) {} by Shaheed Haque (srhaque@theiet.org) +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU Library General Public License as +// published by the Free Software Foundation; either version 2, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details +// +// You should have received a copy of the GNU Library General Public +// License along with this program; if not, write to the +// Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +%End +// +""" + return template.format(output_file, package, h_file, datetime.datetime.utcnow().year) + + +def get_platform_dependencies(working_dir): + """ + Find the system include directories and libclang.so. + """ + data = rules_engine.get_platform_dependencies(os.path.dirname(os.path.realpath(__file__)), working_dir) + sys_includes = data["ClangPP_SYS_INCLUDES"].split(";") + sys_includes = [str(os.path.normpath(i)) for i in sys_includes] + if not sys_includes: + raise RuntimeError(_("Cannot find system includes")) + lib_clang = data["LibClang_LIBRARY"] + exe_clang = data["ClangPP_EXECUTABLE"] + exe_sip = data["SIP_EXECUTABLE"] + logger.info(_("Found Clang: {}, {}, {}").format(lib_clang, exe_clang, sys_includes)) + logger.info(_("Found SIP: {}").format(exe_sip)) + return lib_clang, exe_clang, sys_includes, exe_sip + + +def main(argv=None): + """ + Convert a whole set of header files and generate the corresponding SIP + files. Beyond simple generation of the SIP files from the corresponding C++ + header files: + + - A set of rules can be used to customise the generated SIP files. + + - For each set of SIP files in a directory, if at least one SIP file + is named like a new-style header (i.e. starts with an upper case + letter, or has no .h suffix), then a "module.sip" is created which + facilitates running the SIP compiler on a set of related files. + + Examples: + + module_generator.py /tmp /usr/include/KF5 + """ + if argv is None: + argv = sys.argv + parser = argparse.ArgumentParser(epilog=inspect.getdoc(main), + formatter_class=HelpFormatter) + parser.add_argument("-v", "--verbose", action="store_true", default=False, help=_("Enable verbose output")) + parser.add_argument("--select", default=".*", type=lambda s: re.compile(s, re.I), + help=_("Regular expression of C++ headers from 'rules-pkg' to be processed")) + parser.add_argument("--omit", default="=Nothing=", type=lambda s: re.compile(s, re.I), + help=_("Regular expression of C++ headers from 'rules-pkg' NOT to be processed")) + parser.add_argument("-j", "--jobs", type=int, default=multiprocessing.cpu_count(), + help=_("Number of parallel jobs, 0 for serial inline operation")) + parser.add_argument("--dump-rule-usage", action="store_true", default=False, + help=_("Emit rule usage statistics")) + parser.add_argument("--dump-includes", action="store_true", default=False, + help=_("Emit diagnostics for include files")) + parser.add_argument("--dump-privates", action="store_true", default=False, + help=_("Emit diagnostics for omitted private items")) + parser.add_argument("--dump-modules", action="store_true", default=False, + help=_("Emit tracing for modules")) + parser.add_argument("--dump-items", action="store_true", default=False, + help=_("Emit tracing for container members")) + parser.add_argument("rules", help=_("Project rules package")) + parser.add_argument("output", help=_("SIP output directory")) + try: + args = parser.parse_args(argv[1:]) + if args.verbose: + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s: %(message)s') + else: + logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') + # + # Generate! + # + rules_pkg = os.path.normpath(args.rules) + output = os.path.normpath(args.output) + d = ModuleGenerator(rules_pkg, output, dump_modules=args.dump_modules, dump_items=args.dump_items, + dump_includes=args.dump_includes, dump_privates=args.dump_privates, verbose=args.verbose) + attempts, failures, directories = d.process_tree(args.jobs, args.select, args.omit) + if args.dump_rule_usage: + for rule in sorted(d.rule_usage.keys()): + usage_count = d.rule_usage[rule] + if usage_count: + logger.info(_("Rule {} used {} times".format(rule, usage_count))) + else: + logger.warn(_("Rule {} was not used".format(rule))) + # + # Dump a summary of what we did. Order the results by the name of the source. + # + for source, e in failures: + logger.info(_("Summary: {}: {} while processing {}").format(e[0].__name__, e[1], source)) + level = logging.ERROR if failures else logging.INFO + logger.log(level, _("Summary: {} processing errors for {} files in {} modules").format(len(failures), attempts, + directories)) + if failures: + tbk = failures[0][1][2] + print(tbk) + return -1 + except Exception as e: + tbk = traceback.format_exc() + print(tbk) + return -1 + + +if __name__ == "__main__": + sys.exit(main()) Index: find-modules/module_generation/rule_helpers.py =================================================================== --- /dev/null +++ find-modules/module_generation/rule_helpers.py @@ -0,0 +1,387 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +"""Some common rule actions, as a convenience for rule writers.""" +import os +import re + +from utils import decompose_template, fqn, trace_inserted_for, trace_discarded_by, trace_generated_for + +ANNOTATIONS_RE = re.compile(" /.*/") + +# +# By default, rules return None. This causes the rule-firing logic to emit +# diagnostics recording what, if anything, changed. Rules which want to +# suppress "nothing changed" messages should return SILENT_NOOP. +# +SILENT_NOOP = "do-not-report-lack-of-changes" + + +def initialise_cxx_decl(sip): + """ + Initialise a C++ declaration. + + :param sip: + :return: Any annotations we found. + """ + annotations = [] + sip["cxx_parameters"] = [] + for p in sip["parameters"]: + a = ANNOTATIONS_RE.search(p) + if a: + a = a.group() + p = ANNOTATIONS_RE.sub("", p) + else: + a = "" + annotations.append(a) + sip["cxx_parameters"].append(p) + sip["cxx_fn_result"] = sip["fn_result"] + return annotations + + +def noop(*args): + """ + This action function "does nothing" but without causing a warning. + For example, using this as the action in an ForwardDeclarationDb entry can + be used to override the default "drop forward declarations" rule. + """ + return SILENT_NOOP + + +def container_discard(container, sip, matcher): + sip["name"] = "" + + +def forward_declaration_discard(container, sip, matcher): + sip["name"] = "" + + +def function_discard(container, function, sip, matcher): + sip["name"] = "" + + +def typedef_discard(container, typedef, sip, matcher): + sip["name"] = "" + + +def variable_discard(container, variable, sip, matcher): + sip["name"] = "" + + +def unexposed_discard(container, unexposed, sip, matcher): + sip["name"] = "" + + +def container_discard_QSharedData_base(container, sip, matcher): + sip["base_specifiers"].remove("QSharedData") + + +def container_mark_abstract(container, sip, matcher): + sip["annotations"].add("Abstract") + + +def forward_declaration_mark_external(container, sip, matcher): + sip["annotations"].add("External") + + +def parameter_in(container, function, parameter, sip, matcher): + sip["annotations"].add("In") + + +def parameter_out(container, function, parameter, sip, matcher): + sip["annotations"].add("Out") + + +def parameter_transfer_to_parent(container, function, parameter, sip, matcher): + if function.is_static_method(): + sip["annotations"].add("Transfer") + else: + sip["annotations"].add("TransferThis") + + +def param_rewrite_mode_t_as_int(container, function, parameter, sip, matcher): + sip["decl"] = sip["decl"].replace("mode_t", "unsigned int") + + +def parameter_strip_class_enum(container, function, parameter, sip, matcher): + sip["decl"] = sip["decl"].replace("class ", "").replace("enum ", "") + + +def modulecode_delete(filename, sip, rule, *keys): + """ + Delete duplicate modulecode entries from the current module. + This prevents clashes when the current module A, imports B and both define + the same thing. + + :param filename: The filename of the module, e.g. KCoreAddonsmod.sip. + :param sip: The sip. + :param rule: The rule. + :param keys: The keys to the entries. + """ + for key in keys: + trace = trace_discarded_by(key, rule) + del sip["modulecode"][key] + sip["modulecode"][key] = trace + + +def modulecode_make_local(filename, sip, rule, *keys): + """ + Make modulecode entries local to the current module using the feature name. + This prevents clashes when the current module A, and another module B both: + + - define the same thing + - are imported by a third module C + + :param filename: The filename of the module, e.g. KCoreAddonsmod.sip. + :param sip: The sip. + :param rule: The rule. + :param keys: The keys to the entries. + """ + feature = os.path.splitext(filename)[0].replace(os.path.sep, "_") + for key in keys: + tmp = sip["modulecode"][key] + trace = trace_inserted_for(key, rule) + tmp = trace + "%If (!" + feature + ")\n" + tmp + "%End\n" + sip["modulecode"][key] = tmp + + +def module_add_classes(filename, sip, rule, *classes): + """ + Add missing class declarations to a module. + + :param filename: The filename of the module, e.g. KCoreAddonsmod.sip. + :param sip: The sip. + :param rule: The rule. + :param classes: The classes to add. + """ + feature = os.path.splitext(filename)[0].replace(os.path.sep, "_") + tmp = "" + for key in classes: + tmp += "class " + key + ";\n" + trace = trace_generated_for(sip["name"], rule, "missing classes") + tmp = trace + "%If (!" + feature + ")\n" + tmp + "%End\n" + sip["code"] += tmp + + +def module_add_imports(filename, sip, rule, *modules): + """ + Add missing imports to a module. + + :param filename: The filename of the module, e.g. KCoreAddonsmod.sip. + :param sip: The sip. + :param rule: The rule. + :param modules: The modules to add. + """ + feature = os.path.splitext(filename)[0].replace(os.path.sep, "_") + tmp = "" + for key in modules: + tmp += "%Import(name=" + key + ")\n" + trace = trace_generated_for(sip["name"], rule, "missing imports") + tmp = trace + "%If (!" + feature + ")\n" + tmp + "%End\n" + sip["code"] += tmp + + +def module_delete_imports(filename, sip, rule, *modules): + """ + Remove unwanted imports from a module. + + :param filename: The filename of the module, e.g. KCoreAddonsmod.sip. + :param sip: The sip. + :param rule: The rule. + :param modules: The modules to remove. + """ + trace = trace_generated_for(sip["name"], rule, "delete imports") + lines = [] + modules = list(modules) + candidates = [] + for l in sip["decl"].split("\n"): + l = l.strip() + if l.startswith("%Import"): + m = l[:-1].split("=", 1)[1] + candidates.append(m) + if m in modules: + modules.remove(m) + lines.append(trace) + lines.append("// " + l) + continue + lines.append(l) + sip["decl"] = "\n".join(lines) + assert not modules, "Modules {} not found in {}".format(modules, candidates) + + +def module_yank_scoped_class(filename, sip, rule): + """ + SIP does not support classes defined with a scoped name, such as A::B. + We physically yank things into place. + """ + child = "^ class " + sip["ctx"]["child"] + "\\b[^\n]*\n {.*?(^ };)$" + parent = "^ class " + sip["ctx"]["parent"] + "\\b[^\n]*\n {.*?(^ };)$" + trace_from = trace_generated_for(sip["name"], rule, "yanking '{}' into '{}'".format(sip["ctx"]["child"], sip["ctx"]["parent"])) + trace_to = trace_generated_for(sip["name"], rule, "yanked '{}' into '{}'".format(sip["ctx"]["child"], sip["ctx"]["parent"])) + # + # + # + child = re.search(child, sip["decl"], re.DOTALL | re.MULTILINE) + tmp = sip["decl"][:child.start(0)] + trace_from[:-1] + sip["decl"][child.end(0):] + parent = re.search(parent, tmp, re.DOTALL | re.MULTILINE) + sip["decl"] = tmp[:parent.start(1)] + trace_to + " public:\n" + child.group(0) + "\n" + tmp[parent.start(1):] + + +def module_add_includes(filename, sip, rule, *includes): + """ + There are many cases where adding a #include is a useful workaround. + + :param filename: The filename of the module, e.g. KCoreAddonsmod.sip. + :param sip: The sip. + :param rule: The rule. + :param includes: The includes to add. + """ + tmp = "" + for key in includes: + tmp += "#include " + key + "\n" + trace = trace_generated_for(sip["name"], rule, "missing includes") + tmp = trace + "%ModuleHeaderCode\n" + tmp + "%End\n" + sip["code"] += tmp + + +def container_add_typedefs(container, sip, rule, *typedefs): + """ + There are many cases of types which SIP cannot handle, but where adding a C++ typedef is a useful workaround. + + :param container: The container in question. + :param sip: The sip. + :param rule: The rule. + :param typedefs: The typedefs to add. + """ + def get_template(typedef, parent): + """ + Any template parameters from the parent in "typedef" must be extracted, + and presented as template parameters for the typedef, so for + + template + class Foo + { + public: + typedef DoesNotUseBorC Bar; + } + + we need to extract to enable: + + template + typedef Foo::Bar; + + TODO: this actually needs to take the templating_stack into account. + """ + if parent.template_parameters is None: + return None + name, args = decompose_template(typedef) + ids = [] + if args: + for arg in args: + ids.extend([a.strip() for a in re.split(",| |::|<|>|\*|&", arg) if a]) + ids.extend(name.split("::")) + ids = [a for a in ids if a in parent.template_parameters] + return ", ".join(ids) + + typeheadercode = "" + trace = trace_generated_for(sip["name"], rule, "supplementary typedefs") + for new_typedef, original_type in enumerate(typedefs): + new_typedef = "__{}{}_t".format(container.spelling, new_typedef) + typedef_template = get_template(original_type, container) + # + # Generate a C++ typedef in %TypeHeaderCode. + # + if typedef_template: + typeheadercode += " template<" + typedef_template + ">\n" + typeheadercode += " typedef " + original_type + " " + new_typedef + ";\n" + sip["body"] = sip["body"].replace(original_type, new_typedef) + sip["modulecode"][new_typedef] = "class " + new_typedef + ";\n" + sip["code"] += trace + "%TypeHeaderCode\n" + typeheadercode + "%End\n" + + +def container_fake_derived_class(container, sip, rule): + """ + There are many cases where SIP does not generate a derived class, and + having a #define to fake one makes writing manual code easier. See + + https://www.riverbankcomputing.com/pipermail/pyqt/2017-June/039309.html + + :param container: The container in question. + :param sip: The sip. + :param rule: The rule. + :param includes: The includes to add. + """ + clazz = fqn(container) + tmp = "#define sip{} {}\n".format(clazz.replace("::", "_"), clazz) + trace = trace_generated_for(sip["name"], rule, "fake derived class") + tmp = trace + "%TypeHeaderCode\n" + tmp + "%End\n" + sip["code"] += tmp + + +def container_discard_templated_bases(container, sip, rule): + """ + SIP cannot handle base templates like "class Foo: Bar" without an + intermediate typedef. For now, delete the base class. See + + https://www.riverbankcomputing.com/pipermail/pyqt/2017-August/039476.html + + :param container: The container in question. + :param sip: The sip. + :param rule: The rule. + :param includes: The includes to add. + """ + sip["base_specifiers"] = [b for b in sip["base_specifiers"] if "<" not in b] + + +def container_make_unassignable(container, sip, rule): + """ + There are cases where the built-in logic cannot detect the need to make a class unassignable. + + :param container: The container in question. + :param sip: The sip. + :param rule: The rule. + """ + clazz = sip["name"] + tmp = " private:\n {} &operator=(const {} &);\n".format(clazz, clazz) + trace = trace_generated_for(sip["name"], rule, "dummy assignment") + tmp = trace + tmp + sip["body"] = tmp + sip["body"] + + +def container_make_uncopyable(container, sip, rule): + """ + There are cases where the built-in logic cannot detect the need to make a class uncopyable. + + :param container: The container in question. + :param sip: The sip. + :param rule: The rule. + """ + clazz = sip["name"] + tmp = " private:\n {}(const {} &);\n".format(clazz, clazz) + trace = trace_generated_for(sip["name"], rule, "dummy copy constructor") + tmp = trace + tmp + sip["body"] = tmp + sip["body"] Index: find-modules/module_generation/rules_engine.py =================================================================== --- /dev/null +++ find-modules/module_generation/rules_engine.py @@ -0,0 +1,1625 @@ +#!/usr/bin/env python +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# Copyright 2016 Stephen Kelly +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +"""SIP file generation rules engine.""" + +from __future__ import print_function + +import argparse +import gettext +import importlib +import inspect +import json +import logging +import os +import re +import shutil +import subprocess +import sys +import tempfile +import textwrap +import traceback +from abc import ABCMeta, abstractmethod +from copy import deepcopy + +import builtin_rules +import rule_helpers +from utils import cursor_parents, trace_inserted_for, trace_generated_for + + +class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): + pass + + +logger = logging.getLogger(__name__) +gettext.install(__name__) +_SEPARATOR = "\x00" + +# Keep PyCharm happy. +_ = _ + +_mapped_type_re = re.compile("^%(ConvertToTypeCode|ConvertFromTypeCode)", re.MULTILINE) + + +def handle_mapped_types(cursor, sip): + # + # Is the resulting code a %MappedType? TODO: Handle combinations of mapped and non-mapped code. + # + mapped_type = _mapped_type_re.search(sip["code"]) + if mapped_type: + name = "{}:{}[{}]".format(cursor.spelling, os.path.basename(cursor.extent.start.file.name), + cursor.extent.start.line) + sip["modulecode"][name] = "%MappedType " + sip["decl"] + "\n{" + sip["code"] + "};\n" + sip["code"] = "" + + +class Rule(object): + def __init__(self, rule_name, fn, pattern_zip): + self.name = "{},{}".format(rule_name, fn.__name__) + self.fn = fn + self.usage = 0 + try: + groups = ["(?P<{}>{})".format(name, pattern) for pattern, name in pattern_zip] + groups = _SEPARATOR.join(groups) + # + # We'll use re.match to anchor the start of the match, and so need a $ to anchor the end. + # + self.matcher = re.compile(groups + "$") + except Exception as e: + groups = ["{} '{}'".format(name, pattern) for pattern, name in pattern_zip] + groups = ", ".join(groups) + raise RuntimeError(_("Bad {}: {}: {}").format(self, groups, e)) + + def match(self, candidate): + return self.matcher.match(candidate) + + def trace_result(self, result, parents, item, original, modified): + """ + Record any modification both in the log and the returned result. If a rule fired, but + caused no modification, that is logged. + + :return: Modifying rule or None. + """ + fqn = parents + "::" + original["name"] + "[" + str(item.extent.start.line) + "]" + return self._trace_result(result, fqn, original, modified) + + def _trace_result(self, result, fqn, original, modified): + """ + Record any modification both in the log and the returned result. If a rule fired, but + caused no modification, that is logged. + + :return: Modifying rule or None. + """ + if not modified["name"]: + logger.debug(_("Rule {} suppressed {}, {}").format(self, fqn, original)) + else: + delta = False + for k, v in original.iteritems(): + if v != modified[k]: + delta = True + break + if delta: + logger.debug(_("Rule {} modified {}, {}->{}").format(self, fqn, original, modified)) + else: + if result != rule_helpers.SILENT_NOOP: + logger.warn(_("Rule {} did not modify {}, {}").format(self, fqn, original)) + return None + return self + + def __str__(self): + return self.name + + +class AbstractCompiledRuleDb(object): + __metaclass__ = ABCMeta + + def __init__(self, raw_rules, parameter_names): + """ + Create a database of compiled rules. + + :param raw_rules: A function which returns an ordered list of raw rules. The point of + using a function is that a function has a name which can be used for + diagnostics. + :parameter_names: The name of each field in the raw rules. + """ + self.compiled_rules = [] + self.parameter_names = parameter_names + self.candidate_formatter = _SEPARATOR.join(["{}"] * len(parameter_names)) + # + # Backwards compatibility. + # + # TODO: Remove when Steve and Shaheed are agreed/ready. + # + if not callable(raw_rules): + tmp = lambda: raw_rules + self.add_rules(tmp) + else: + self.add_rules(raw_rules) + + def _match(self, *args): + candidate = self.candidate_formatter.format(*args) + for rule in self.compiled_rules: + matcher = rule.match(candidate) + if matcher: + # + # Only use the first matching rule. + # + rule.usage += 1 + return matcher, rule + return None, None + + def add_rules(self, raw_rules): + """ + Add to the existing set of rules. The new rules have precedence over existing rules. + + :param raw_rules: A function which returns an ordered list of raw rules. The point of + using a function is that a function has a name which can be used for + diagnostics. May be None. + """ + rule_batch = AbstractCompiledRuleDb.batch_name(raw_rules, inspect.stack()[4]) + if not rule_batch: + return + tmp = [] + for i, raw_rule in enumerate(raw_rules()): + # + # Derive a useful name for diagnostic purposes. + # + rule_name = "{}[{}]".format(rule_batch, i) + # + # Backwards compatibility. Older rule databases will be missing entries for (fn_result, decl) and + # (prefix, suffix) for TypedefRuleDb and FunctionRuleDb respectively. + # + # TODO: Remove when Steve and Shaheed are agreed/ready. + # + if len(raw_rule) == len(self.parameter_names) - 1 and isinstance(self, (TypedefRuleDb, FunctionRuleDb)): + raw_rule = raw_rule[:-1] + [".*", ".*"] + [raw_rule[-1]] + if len(raw_rule) != len(self.parameter_names) + 1: + raise RuntimeError(_("Bad raw rule {}: {}: {}").format(rule_name, raw_rule, self.parameter_names)) + z = zip(raw_rule[:-1], self.parameter_names) + tmp.append(Rule(rule_name, raw_rule[-1], z)) + self.compiled_rules = tmp + self.compiled_rules + + @staticmethod + def batch_name(raw_rules, caller): + """ + Derive a useful name for a batch of rules. + + :param raw_rules: A function which returns an ordered list of raw rules. The point of + using a function is that a function has a name which can be used for + diagnostics. May be None. + :param caller: Backwards compatibility support. + :return: + """ + if raw_rules is None or raw_rules() is None: + return "" + rule_file = inspect.getfile(raw_rules) + if rule_file in [__file__, __file__[:-1]]: + # + # We must have come through the backwards compatibility in __init__. + # + # TODO: Remove when Steve and Shaheed are agreed/ready. + # + rule_file = caller[1] + rule_id = str(caller[2]) + else: + rule_id = raw_rules.__name__ + rule_file = os.path.basename(rule_file) + return "{}:{}".format(rule_file, rule_id) + + @abstractmethod + def apply(self, *args): + raise NotImplemented(_("Missing subclass")) + + def dump_usage(self, fn): + """ Dump the usage counts.""" + for rule in self.compiled_rules: + fn(str(rule), rule.usage) + + +class ContainerRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR CONTAINERS. + + These are used to customise the behaviour of the SIP generator by allowing + the declaration for any container (class, namespace, struct, union) to be + customised, for example to add SIP compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the container. + + 1. A regular expression which matches the container name. + + 2. A regular expression which matches any template parameters. + + 3. A regular expression which matches the container declaration. + + 4. A regular expression which matches any base specifiers. + + 5. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def container_xxx(container, sip, rule): + ''' + Return a modified declaration for the given container. + + :param container: The clang.cindex.Cursor for the container. + :param sip: A dict with the following keys: + + name The name of the container. + template_parameters Any template parameters. + decl The declaration. + base_specifiers Any base specifiers. + body The body, less the outer + pair of braces. + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the container to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(ContainerRuleDb, self).__init__(db, ["parents", "container", "template_parameters", "decl", "base_specifiers"]) + + def apply(self, container, sip): + """ + Walk over the rules database for containers, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(container) + matcher, rule = self._match(parents, sip["name"], + ", ".join(sip["template_parameters"] or []), + sip["decl"], + ", ".join(sip["base_specifiers"])) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if matcher: + before = deepcopy(sip) + result = rule.fn(container, sip, rule) + return rule.trace_result(result, parents, container, before, sip) + return None + + +class ForwardDeclarationRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR FORWARD DECLARATIONS. + + These are used to customise the behaviour of the SIP generator by allowing + the forward declaration for any container (class, struct, union) to be + customised, for example to add SIP compiler annotations. + + For example, it is not appropriate to annotate each forward declaration with + /External/ as this is only needed for types which are defined in a different + module, so the SIP generator omits them. (Local forward declarations are not + needed). So, rules can be added here to emit /External/ as needed. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the container. + + 1. A regular expression which matches the container name. + + 2. A regular expression which matches any template parameters. + + 3. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def declaration_xxx(container, sip, rule): + ''' + Return a modified declaration for the given container. + + :param container: The clang.cindex.Cursor for the container. + :param sip: A dict with the following keys: + + name The name of the container. + template_parameters Any template parameters. + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the container to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(ForwardDeclarationRuleDb, self).__init__(db, ["parents", "container", "template_parameters"]) + + def apply(self, container, sip): + """ + Walk over the rules database for containers, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(container) + matcher, rule = self._match(parents, sip["name"], + ", ".join(sip["template_parameters"] or [])) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if matcher: + before = deepcopy(sip) + result = rule.fn(container, sip, rule) + handle_mapped_types(container, sip) + return rule.trace_result(result, parents, container, before, sip) + return None + + +class FunctionRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR FUNCTIONS. + + These are used to customise the behaviour of the SIP generator by allowing + the declaration for any function to be customised, for example to add SIP + compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the function. + + 1. A regular expression which matches the function name. + + 2. A regular expression which matches any template parameters. + + 3. A regular expression which matches the function result. + + 4. A regular expression which matches the function parameters (e.g. + "int a, void *b" for "int foo(int a, void *b)"). + + 5. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def function_xxx(container, function, sip, rule): + ''' + Return a modified declaration for the given function. + + :param container: The clang.cindex.Cursor for the container. + :param function: The clang.cindex.Cursor for the function. + :param sip: A dict with the following keys: + + name The name of the function. + template_parameters Any template parameters. + fn_result Result, if not a constructor. + parameters The parameters. + prefix Leading keyworks ("static"). Separated by space, + ends with a space. + suffix Trailing keywords ("const"). Separated by space, starts with + space. + is_signal Is this is a signal? + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the container to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(FunctionRuleDb, self).__init__(db, ["container", "function", "template_parameters", "fn_result", + "parameters", "prefix", "suffix"]) + + def apply(self, container, function, sip): + """ + Walk over the rules database for functions, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param function: The clang.cindex.Cursor for the function. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(function) + matcher, rule = self._match(parents, sip["name"], ", ".join(sip["template_parameters"] or []), sip["fn_result"], + ", ".join(sip["parameters"]), sip["prefix"], sip["suffix"]) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + sip.setdefault("cxx_parameters", "") + sip.setdefault("cxx_fn_result", "") + if matcher: + before = deepcopy(sip) + result = rule.fn(container, function, sip, rule) + handle_mapped_types(function, sip) + return rule.trace_result(result, parents, function, before, sip) + return None + + +class ParameterRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR FUNCTION PARAMETERS. + + These are used to customise the behaviour of the SIP generator by allowing + the declaration for any parameter in any function to be customised, for + example to add SIP compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the function enclosing the parameter. + + 1. A regular expression which matches the function name enclosing the + parameter. + + 2. A regular expression which matches the parameter name. + + 3. A regular expression which matches the parameter declaration (e.g. + "int foo"). + + 4. A regular expression which matches the parameter initialiser (e.g. + "Xyz:MYCONST + 42"). + + 5. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def parameter_xxx(container, function, parameter, sip, init, rule): + ''' + Return a modified declaration and initialiser for the given parameter. + + :param container: The clang.cindex.Cursor for the container. + :param function: The clang.cindex.Cursor for the function. + :param parameter: The clang.cindex.Cursor for the parameter. + :param sip: A dict with the following keys: + + name The name of the parameter. + decl The declaration. + init Any initialiser. + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(ParameterRuleDb, self).__init__(db, ["container", "function", "parameter", "decl", "init"]) + + def apply(self, container, function, parameter, sip): + """ + Walk over the rules database for parameters, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param function: The clang.cindex.Cursor for the function. + :param parameter: The clang.cindex.Cursor for the parameter. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(function) + matcher, rule = self._match(parents, function.spelling, sip["name"], sip["decl"], sip["init"]) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if matcher: + before = deepcopy(sip) + result = rule.fn(container, function, parameter, sip, rule) + handle_mapped_types(parameter, sip) + return rule.trace_result(result, parents, parameter, before, sip) + return None + + +class TypedefRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR TYPEDEFS. + + These are used to customise the behaviour of the SIP generator by allowing + the declaration for any typedef to be customised, for example to add SIP + compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the typedef. + + 1. A regular expression which matches the typedef name. + + 2. A regular expression which matches the function result for a function + pointer typedef. + + 3. A regular expression which matches the typedef declaration (e.g. + "typedef int foo"). + + 4. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def typedef_xxx(container, typedef, sip, rule): + ''' + Return a modified declaration for the given function. + + :param container: The clang.cindex.Cursor for the container. + :param typedef: The clang.cindex.Cursor for the typedef. + :param sip: A dict with the following keys: + + name The name of the typedef. + fn_result Result, for a function pointer. + decl The declaration. + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the container to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(TypedefRuleDb, self).__init__(db, ["container", "typedef", "fn_result", "decl"]) + + def apply(self, container, typedef, sip): + """ + Walk over the rules database for typedefs, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param typedef: The clang.cindex.Cursor for the typedef. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(typedef) + matcher, rule = self._match(parents, sip["name"], sip["fn_result"], sip["decl"]) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if matcher: + before = deepcopy(sip) + result = rule.fn(container, typedef, sip, rule) + handle_mapped_types(typedef, sip) + return rule.trace_result(result, parents, typedef, before, sip) + return None + + +class UnexposedRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR UNEXPOSED ITEMS. + + These are used to customise the behaviour of the SIP generator by allowing + the declaration for any unexposed item to be customised, for example to + add SIP compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the unexposed item. + + 1. A regular expression which matches the unexposed item name. + + 2. A regular expression which matches the unexposed item declaration. + + 3. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def unexposed_xxx(container, unexposed, sip, rule): + ''' + Return a modified declaration for the given container. + + :param container: The clang.cindex.Cursor for the container. + :param unexposed: The clang.cindex.Cursor for the unexposed item. + :param sip: A dict with the following keys: + + name The name of the unexposed item. + decl The declaration. + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the unexposed item to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(UnexposedRuleDb, self).__init__(db, ["container", "unexposed", "decl"]) + + def apply(self, container, unexposed, sip): + """ + Walk over the rules database for unexposed items, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param unexposed: The clang.cindex.Cursor for the unexposed item. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(unexposed) + matcher, rule = self._match(parents, sip["name"], sip["decl"]) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if matcher: + before = deepcopy(sip) + result = rule.fn(container, unexposed, sip, rule) + handle_mapped_types(unexposed, sip) + return rule.trace_result(result, parents, unexposed, before, sip) + return None + + +class VariableRuleDb(AbstractCompiledRuleDb): + """ + THE RULES FOR VARIABLES. + + These are used to customise the behaviour of the SIP generator by allowing + the declaration for any variable to be customised, for example to add SIP + compiler annotations. + + Each entry in the raw rule database must be a list with members as follows: + + 0. A regular expression which matches the fully-qualified name of the + "container" enclosing the variable. + + 1. A regular expression which matches the variable name. + + 2. A regular expression which matches the variable declaration (e.g. + "int foo"). + + 3. A function. + + In use, the database is walked in order from the first entry. If the regular + expressions are matched, the function is called, and no further entries are + walked. The function is called with the following contract: + + def variable_xxx(container, variable, sip, rule): + ''' + Return a modified declaration for the given variable. + + :param container: The clang.cindex.Cursor for the container. + :param variable: The clang.cindex.Cursor for the variable. + :param sip: A dict with the following keys: + + name The name of the variable. + decl The declaration. + annotations Any SIP annotations. + + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. Setting sip.name to the + empty string will cause the container to be suppressed. + ''' + + :return: The compiled form of the rules. + """ + def __init__(self, db): + super(VariableRuleDb, self).__init__(db, ["container", "variable", "decl"]) + + def apply(self, container, variable, sip): + """ + Walk over the rules database for variables, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param variable: The clang.cindex.Cursor for the variable. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + parents = cursor_parents(variable) + matcher, rule = self._match(parents, sip["name"], sip["decl"]) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if matcher: + before = deepcopy(sip) + result = rule.fn(container, variable, sip, rule) + handle_mapped_types(variable, sip) + return rule.trace_result(result, parents, variable, before, sip) + return None + + +class AbstractCompiledCodeDb(object): + __metaclass__ = ABCMeta + + def __init__(self, raw_rules): + """ + Create a database of compiled rules. + + :param raw_rules: A dict of raw rules, or a function which returns a dict of raw rules. + The point of using a function is that a function has a name which can + be used for diagnostics. + """ + self.compiled_rules = {} + self.names = [] + # + # Backwards compatibility. + # + # TODO: Remove when Steve and Shaheed are agreed/ready. + # + if not callable(raw_rules): + tmp = lambda: raw_rules + self.add_rules(tmp) + else: + self.add_rules(raw_rules) + + def add_rules(self, raw_rules): + """ + Add to the existing set of rules. The new rules have precedence over existing rules. + + :param raw_rules: A function which returns a dict of raw rules. The point of using a + function is that a function has a name which can be used for + diagnostics. May be None. + """ + rule_batch = AbstractCompiledRuleDb.batch_name(raw_rules, inspect.stack()[4]) + if not rule_batch: + return False + self.names.append(rule_batch) + for k, v in raw_rules().items(): + self._check_rule(rule_batch, k, v) + if k in self.compiled_rules: + logger.debug(_("Updating raw rule {}").format(k)) + self.compiled_rules[k] = v + return True + + def _check_rule(self, rule_batch, k, v): + if "code" not in v: + raise RuntimeError(_("Bad raw rule {}: {}: {}").format(rule_batch, k, v.keys())) + + @abstractmethod + def apply(self, function, sip): + raise NotImplemented(_("Missing subclass")) + + def trace_result(self, result, rule, parents, item, original, modified): + """ + Record any modification both in the log and the returned result. If a rule fired, but + caused no modification, that is logged. + + :return: Modifying rule or None. + """ + fqn = parents + "::" + original["name"] + "[" + str(item.extent.start.line) + "]" + return self._trace_result(result, rule, fqn, original, modified) + + def _trace_result(self, result, rule, fqn, original, modified): + """ + Record any modification both in the log and the returned result. If a rule fired, but + caused no modification, that is logged. + + :return: Modifying rule or None. + """ + ruleset = self.names[rule["ruleset"]] + if not modified["name"]: + logger.debug(_("Rule {} discarded {}, {}").format(ruleset, fqn, original)) + else: + delta = False + for k, v in original.iteritems(): + if v != modified[k]: + delta = True + break + if delta: + logger.debug(_("Rule {} modified {}, {}->{}").format(ruleset, fqn, original, modified)) + else: + if result != rule_helpers.SILENT_NOOP: + logger.warn(_("Rule {} did not modify {}, {}").format(ruleset, fqn, original)) + return None + return ruleset + + @abstractmethod + def dump_usage(self, fn): + raise NotImplemented(_("Missing subclass")) + + +class MethodCodeDb(AbstractCompiledCodeDb): + """ + THE RULES FOR INJECTING METHOD-RELATED CODE (such as %MethodCode, + %VirtualCatcherCode, %VirtualCallCode and other method-level directives). + + These are used to customise the behaviour of the SIP generator by allowing + method-level code injection. + + The raw rule database must be an outer dictionary as follows: + + 0. Each key is the fully-qualified name of a "container" enclosing + methods. + + 1. Each value is an inner dictionary, each of whose keys is the name + of a method. + + Each inner dictionary has entries which update the declaration as follows: + + "name": Optional string. If present, overrides the name of the + method. + + "parameters": Optional list. If present, update the argument list. + + "fn_result": Optional string. If present, update the return type. + + "cxx_parameters", "cxx_fn_result" + Both optional. If either is present, the SIP method's + optional C++ declaration is added (if only one is + present, "cxx_parameters" is defaulted from "parameters" and + "cxx_fn_result" is defaulted from "fn_result"). + + "code": Required. Either a string, with the %XXXCode content, + or a callable. + + "ctx": Optional user-defined value. If present, copied to the sip. + + In use, the database is directly indexed by "container" and then method + name. If "code" entry is a string, then the other optional keys are + interpreted as above. If "code" is a callable, it is called with the + following contract: + + def methodcode_xxx(function, sip, rule): + ''' + Return a modified declaration for the given function. + + :param function: The clang.cindex.Cursor for the function. + :param sip: A dict with keys as for function rules + plus the "cxx_parameters", "cxx_fn_result" and (string) + "code" keys described above. + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. + ''' + + :return: The compiled form of the rules. + """ + __metaclass__ = ABCMeta + + def __init__(self, raw_rules): + super(MethodCodeDb, self).__init__(raw_rules) + + def add_rules(self, raw_rules): + if not super(MethodCodeDb, self).add_rules(raw_rules): + return + # + # Add a usage count and other diagnostic support for each item in the database. + # + ruleset = len(self.names) - 1 + for k, v in raw_rules().items(): + for l in v.keys(): + self.compiled_rules[k][l]["usage"] = 0 + self.compiled_rules[k][l]["ruleset"] = ruleset + + def _check_rule(self, rule_batch, k, v): + for method, vv in v.items(): + if "code" not in vv: + raise RuntimeError(_("Bad raw rule {}: {}: {}").format(rule_batch, k, vv.keys())) + + def _get(self, item, name): + # + # Lookup any parent-level entries. + # + parents = cursor_parents(item) + entries = self.compiled_rules.get(parents, None) + if not entries: + return None + # + # Now look for an actual hit. + # + entry = entries.get(name, None) + if not entry: + return None + entry["usage"] += 1 + return entry + + def apply(self, function, sip): + """ + Walk over the code database for functions, applying the first matching transformation. + + :param function: The clang.cindex.Cursor for the function. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + entry = self._get(function, sip["name"]) + # + # SIP supports the notion of a second C++ signature as well as the normal signature. By default, this + # is not present. + # + sip.setdefault("cxx_parameters", "") + sip.setdefault("cxx_fn_result", "") + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if entry: + rule = self.names[entry["ruleset"]] + ":" + sip["name"] + before = deepcopy(sip) + sip["ctx"] = entry.get("ctx", None) + if callable(entry["code"]): + fn = entry["code"] + trace = trace_generated_for(function, rule, {}) + result = fn(function, sip, rule) + else: + trace = trace_inserted_for(function, rule) + sip["name"] = entry.get("name", sip["name"]) + sip["code"] = entry["code"] + sip["parameters"] = entry.get("parameters", sip["parameters"]) + sip["fn_result"] = entry.get("fn_result", sip["fn_result"]) + # + # The user might provide one or other or both of cxx_parameters and cxx_fn_result to signify a C++ signature. If + # needed, default a missing value from decl/fn_result. + # + if "cxx_parameters" in entry or "cxx_fn_result" in entry: + sip["cxx_parameters"] = entry.get("cxx_parameters", sip["parameters"]) + sip["cxx_fn_result"] = entry.get("cxx_fn_result", sip["cxx_fn_result"]) + result = None + # + # Fetch/format the code. + # + sip["code"] = textwrap.dedent(sip["code"]).strip() + "\n" + sip["code"] = trace + sip["code"] + handle_mapped_types(function, sip) + return self.trace_result(result, entry, cursor_parents(function), function, before, sip) + return None + + def dump_usage(self, fn): + """ Dump the usage counts.""" + for k in sorted(self.compiled_rules.keys()): + vk = self.compiled_rules[k] + for l in sorted(vk.keys()): + vl = vk[l] + fn(self.names[vl["ruleset"]] + " for " + k + "," + l, vl["usage"]) + + +class TypeCodeDb(AbstractCompiledCodeDb): + """ + THE RULES FOR INJECTING TYPE-RELATED CODE (such as %BIGetBufferCode, + %BIGetReadBufferCode, %BIGetWriteBufferCode, %BIGetSegCountCode, + %BIGetCharBufferCode, %BIReleaseBufferCode, %ConvertFromTypeCode, + %ConvertToSubClassCode, %ConvertToTypeCode, %GCClearCode, %GCTraverseCode, + %InstanceCode, %PickleCode, %TypeCode, %TypeHeaderCode or other type-level + directives). + + These are used to customise the behaviour of the SIP generator by allowing + type-level code injection. + + The raw rule database must be a dictionary as follows: + + 0. Each key is the fully-qualified name of a "container" class, + struct, namespace etc. + + 1. Each value has entries which update the declaration as follows: + + "name": Optional string. If present, overrides the name of the + typedef as the name of the %MappedType. Useful for + adding %MappedType entries for parameters where there + is no explicit typedef. + + "code": Required. Either a string, with the %XXXCode content, + or a callable. + + "ctx": Optional user-defined value. If present, copied to the sip. + + In use, the database is directly indexed by "container". If "code" entry + is a string, it is used directly. Note that the use of any of + %TypeHeaderCode, %ConvertToTypeCode or %ConvertFromTypeCode will cause the + container type to be marked as a %MappedType. If "code" is a callable, + it is called with the following contract: + + def typecode_xxx(container, sip, rule): + ''' + Return a modified declaration for the given function. + + :param container: The clang.cindex.Cursor for the container. + :param sip: A dict with keys as for container rules + plus the "code" key described above. + :param rule: The fired rule. + + :return: An updated set of sip.xxx values. + ''' + + :return: The compiled form of the rules. + """ + __metaclass__ = ABCMeta + + def __init__(self, raw_rules): + super(TypeCodeDb, self).__init__(raw_rules) + + def add_rules(self, raw_rules): + if not super(TypeCodeDb, self).add_rules(raw_rules): + return + # + # Add a usage count and other diagnostic support for each item in the database. + # + ruleset = len(self.names) - 1 + for k, v in raw_rules().items(): + self.compiled_rules[k]["usage"] = 0 + self.compiled_rules[k]["ruleset"] = ruleset + + def _get(self, item, name): + # + # Lookup for an actual hit. + # + parents = cursor_parents(item) + entry = self.compiled_rules.get(parents + "::" + name, None) + if not entry: + return None + entry["usage"] += 1 + return entry + + def apply(self, container, sip): + """ + Walk over the code database for containers, applying the first matching transformation. + + :param container: The clang.cindex.Cursor for the container. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + entry = self._get(container, sip["name"]) + sip.setdefault("code", "") + sip.setdefault("modulecode", {}) + if entry: + rule = self.names[entry["ruleset"]] + ":" + sip["name"] + before = deepcopy(sip) + sip["ctx"] = entry.get("ctx", None) + if callable(entry["code"]): + fn = entry["code"] + trace = trace_generated_for(container, rule, {}) + result = fn(container, sip, rule) + else: + trace = trace_inserted_for(container, rule) + sip["name"] = entry.get("name", sip["name"]) + sip["code"] = entry["code"] + sip["decl"] = entry.get("decl", sip["decl"]) + result = None + # + # Fetch/format the code. + # + sip["code"] = textwrap.dedent(sip["code"]).strip() + "\n" + sip["code"] = trace + sip["code"] + handle_mapped_types(container, sip) + return self.trace_result(result, entry, cursor_parents(container), container, before, sip) + return None + + def dump_usage(self, fn): + """ Dump the usage counts.""" + for k in sorted(self.compiled_rules.keys()): + v = self.compiled_rules[k] + fn(self.names[v["ruleset"]] + " for " + k, v["usage"]) + + +class ModuleCodeDb(AbstractCompiledCodeDb): + """ + THE RULES FOR INJECTING MODULE-RELATED CODE (such as %ExportedHeaderCode, + %ModuleCode, %ModuleHeaderCode or other module-level directives). + + These are used to customise the behaviour of the SIP generator by allowing + module-level code injection. + + The raw rule database must be a dictionary as follows: + + 0. Each key is the basename of a header file. + + 1. Each value has entries which update the declaration as follows: + + "code": Required. Either a string, with the %XXXCode content, + or a callable. + + "ctx": Optional user-defined value. If present, copied to the sip. + + If "code" is a callable, it is called with the following contract: + + def module_xxx(filename, sip, rule): + ''' + Return a string to insert for the file. + + :param filename: The filename. + :param sip: A dict with the key "name" for the filename, + "decl" for the module body plus the "code" key + described above. + :param rule: The fired rule. + + :return: A string. + ''' + + :return: The compiled form of the rules. + """ + __metaclass__ = ABCMeta + + def __init__(self, raw_rules): + super(ModuleCodeDb, self).__init__(raw_rules) + + def add_rules(self, raw_rules): + if not super(ModuleCodeDb, self).add_rules(raw_rules): + return + # + # Add a usage count and other diagnostic support for each item in the database. + # + ruleset = len(self.names) - 1 + for k, v in raw_rules().items(): + self.compiled_rules[k]["usage"] = 0 + self.compiled_rules[k]["ruleset"] = ruleset + + def _get(self, filename): + # + # Lookup for an actual hit. + # + entry = self.compiled_rules.get(filename, None) + if not entry: + return None + entry["usage"] += 1 + return entry + + def apply(self, filename, sip): + """ + Walk over the code database for modules, applying the first matching transformation. + + :param filename: The file for the module. + :param sip: The SIP dict (may be modified on return). + :return: Modifying rule or None (even if a rule matched, it may not modify things). + """ + entry = self._get(filename) + sip.setdefault("code", "") + if entry: + rule = self.names[entry["ruleset"]] + ":" + filename + before = deepcopy(sip) + sip["ctx"] = entry.get("ctx", None) + if callable(entry["code"]): + fn = entry["code"] + trace = trace_generated_for(filename, rule, {}) + result = fn(filename, sip, rule) + else: + trace = trace_inserted_for(filename, rule) + sip["code"] = entry["code"] + sip["decl"] = entry.get("decl", sip["decl"]) + # + # Module-level support. + # + tmp = sip.get("modulecode", None) + if tmp: + sip["modulecode"] = entry.get("modulecode", tmp) + tmp = sip.get("peers", None) + if tmp: + sip["peers"] = entry.get("peers", tmp) + result = None + # + # Fetch/format the code. + # + sip["code"] = textwrap.dedent(sip["code"]).strip() + "\n" + sip["code"] = trace + sip["code"] + fqn = filename + return self._trace_result(result, entry, fqn, before, sip) + return None + + def dump_usage(self, fn): + """ Dump the usage counts.""" + for k in sorted(self.compiled_rules.keys()): + v = self.compiled_rules[k] + fn(self.names[v["ruleset"]] + " for " + k, v["usage"]) + + +class RuleSet(object): + """ + To implement your own binding, create a subclass of RuleSet, also called + RuleSet in your own Python module. Your subclass will expose the raw rules + along with other ancilliary data exposed through the subclass methods. + + You then simply run the SIP generation and SIP compilation programs passing + in the name of your rules file + """ + def __init__(self, rules_module=None, container_rules=None, forward_declaration_rules=None, + function_rules=None, parameter_rules=None, typedef_rules=None, + unexposed_rules=None, variable_rules=None, methodcode=None, + modulecode=None, typecode=None): + """ + Constructor. + + :param rules_module: For convenience, just pass the + Python module containing + rule-returning functions named as + per the remaining arguments. + :param container_rules: + :param forward_declaration_rules: + :param function_rules: + :param parameter_rules: + :param typedef_rules: + :param unexposed_rules: + :param variable_rules: + :param methodcode: + :param modulecode: + :param typecode: + """ + self._container_db = ContainerRuleDb(None) + self._forward_declaration_db = ForwardDeclarationRuleDb(None) + self._fn_db = FunctionRuleDb(None) + self._param_db = ParameterRuleDb(None) + self._typedef_db = TypedefRuleDb(None) + self._unexposed_db = UnexposedRuleDb(None) + self._var_db = VariableRuleDb(None) + self._methodcode = MethodCodeDb(None) + self._modulecode = ModuleCodeDb(None) + self._typecode = TypeCodeDb(None) + # + # Built-in rules go first. + # + self.add_rules(rules_module=builtin_rules) + # + # User supplied rules. + # + self.add_rules(rules_module=rules_module, container_rules=container_rules, + forward_declaration_rules=forward_declaration_rules, function_rules=function_rules, + parameter_rules=parameter_rules, typedef_rules=typedef_rules, unexposed_rules=unexposed_rules, + variable_rules=variable_rules, methodcode=methodcode, modulecode=modulecode, typecode=typecode) + + def add_rules(self, rules_module=None, container_rules=None, forward_declaration_rules=None, + function_rules=None, parameter_rules=None, typedef_rules=None, + unexposed_rules=None, variable_rules=None, methodcode=None, + modulecode=None, typecode=None): + """ + Add a set of rules. + + :param rules_module: For convenience, just pass the + Python module containing + rule-returning functions named as + per the remaining arguments. + :param container_rules: + :param forward_declaration_rules: + :param function_rules: + :param parameter_rules: + :param typedef_rules: + :param unexposed_rules: + :param variable_rules: + :param methodcode: + :param modulecode: + :param typecode: + """ + if rules_module: + container_rules = getattr(rules_module, "container_rules", container_rules) + forward_declaration_rules = getattr(rules_module, "forward_declaration_rules", forward_declaration_rules) + function_rules = getattr(rules_module, "function_rules", function_rules) + parameter_rules = getattr(rules_module, "parameter_rules", parameter_rules) + typedef_rules = getattr(rules_module, "typedef_rules", typedef_rules) + unexposed_rules = getattr(rules_module, "unexposed_rules", unexposed_rules) + variable_rules = getattr(rules_module, "variable_rules", variable_rules) + methodcode = getattr(rules_module, "methodcode", methodcode) + modulecode = getattr(rules_module, "modulecode", modulecode) + typecode = getattr(rules_module, "typecode", typecode) + self._container_db.add_rules(container_rules) + self._forward_declaration_db.add_rules(forward_declaration_rules), + self._fn_db.add_rules(function_rules) + self._param_db.add_rules(parameter_rules) + self._typedef_db.add_rules(typedef_rules) + self._unexposed_db.add_rules(unexposed_rules) + self._var_db.add_rules(variable_rules) + self._methodcode.add_rules(methodcode) + self._modulecode.add_rules(modulecode) + self._typecode.add_rules(typecode) + + def container_rules(self): + """ + Return a compiled list of rules for containers. + + :return: A ContainerRuleDb instance + """ + return self._container_db + + def forward_declaration_rules(self): + """ + Return a compiled list of rules for containers. + + :return: A ForwardDeclarationRuleDb instance + """ + + return self._forward_declaration_db + + def function_rules(self): + """ + Return a compiled list of rules for functions. + + :return: A FunctionRuleDb instance + """ + return self._fn_db + + def parameter_rules(self): + """ + Return a compiled list of rules for function parameters. + + :return: A ParameterRuleDb instance + """ + return self._param_db + + def typedef_rules(self): + """ + Return a compiled list of rules for typedefs. + + :return: A TypedefRuleDb instance + """ + return self._typedef_db + + def unexposed_rules(self): + """ + Return a compiled list of rules for unexposed itesm. + + :return: An UnexposedRuleDb instance + """ + return self._unexposed_db + + def variable_rules(self): + """ + Return a compiled list of rules for variables. + + :return: A VariableRuleDb instance + """ + return self._var_db + + def methodcode_rules(self): + """ + Return a compiled list of rules for method-related code. + + :return: A MethodCodeDb instance + """ + return self._methodcode + + def modulecode_rules(self): + """ + Return a compiled list of rules for module-related code. + + :return: A ModuleCodeDb instance + """ + return self._modulecode + + def typecode_rules(self): + """ + Return a compiled list of rules for type-related code. + + :return: A TypeCodeDb instance + """ + return self._typecode + + def methodcode(self, function, sip): + """ + Lookup %MethodCode. + """ + return self._methodcode.apply(function, sip) + + def modulecode(self, filename, sip): + """ + Lookup %ModuleCode and friends. + """ + return self._modulecode.apply(filename, sip) + + def typecode(self, container, sip): + """ + Lookup %TypeCode and friends. Return True or False depending on whether a + %MappedType is implied. + """ + return self._typecode.apply(container, sip) + + def cxx_source_root(self): + """ + The root of C++ header directories to process to generate SIP. + + :return: Absolute directory path. + """ + raise NotImplementedError() + + def cxx_sources(self): + """ + The individual C++ header subdirectories to process. + If None is returned, then all subdirectories of cxx_source_root() + are processed. + + :return: [Absolute directory path...] or None. + """ + raise NotImplementedError() + + def cxx_includes(self): + """ + Where are the .h files we will need to include? + + :return: [Absolute directory path...] + """ + raise NotImplementedError() + + def cxx_compile_flags(self): + """ + Where are the .h files we will need to include? + + :return: [flag...] + """ + raise NotImplementedError() + + def cxx_libraries(self): + """ + Glob patterns of the libraries we will need to link against. + + :return: [Absolute library glob...]. + """ + raise NotImplementedError() + + @property + def cxx_selector(self): + """ + Regex of items to include. + + :return: regex. + """ + return re.compile(".*") + + @property + def cxx_omitter(self): + """ + Regex of items to omit. + + :return: regex. + """ + return re.compile("=Nothing=") + + def sip_package(self): + """ + The name of the SIP/Python package to create. + + :return: Package name. + """ + raise NotImplementedError() + + def sip_imports(self): + """ + The root of SIP module directories to import. + + :return: [Absolute directory path...] + """ + raise NotImplementedError() + + def dump_unused(self, fn=None): + """ + Usage statistics, to identify unused rules. + + :param fn: An optional callback which takes (rule, usage_count) arguments. + By default, output will be to the logger. + """ + def dumper(rule, usage_count): + if usage_count: + logger.info(_("Rule {} used {} times".format(rule, usage_count))) + else: + logger.warn(_("Rule {} was not used".format(rule))) + + if fn == None: + fn = dumper + for db in [self.container_rules(), self.forward_declaration_rules(), self.function_rules(), + self.parameter_rules(), self.typedef_rules(), self.unexposed_rules(), self.variable_rules(), + self.methodcode_rules(), self.modulecode_rules(), self.typecode_rules()]: + db.dump_usage(fn) + + +def rules(rules_pkg): + """ + Constructor. + + :param rules_pkg: The rules package for the project. + """ + sys.path.insert(0, os.path.dirname(rules_pkg)) + rules_pkg = importlib.import_module(os.path.basename(rules_pkg)) + # + # Statically prepare the rule logic. This takes the rules provided by the user and turns them into code. + # + return rules_pkg.RuleSet() + + +def get_platform_dependencies(for_dir, cache_dir=None): + """ + Return a dictionary of platform-dependent values for a directory. + We presently use CMake to do the heavy lifting. + + :param for_dir: The directory. This is expected to contain + a CMakeLists.txt which, when run, produces + ${CMAKE_CURRENT_BINARY_DIR}/configure.json. + This should contain a JSON object. + :return: The JSON object converted into Python, but with any unicode + converted to str, and any dict items with value ("$", None) removed. + """ + def normalise(obj): + """ + Unicode -> string for JSON loader. We also remove any dictionary items ("$", None). + TODO: Python3 shortcircuit? + """ + if isinstance(obj, unicode): + obj = str(obj) + elif isinstance(obj, dict): + obj = {normalise(k): normalise(v) for k,v in obj.items() if v is not None or str(k) != "$"} + elif isinstance(obj, list): + for i in range(len(obj)): + obj[i] = normalise(obj[i]) + return obj + + if cache_dir: + # + # Create cache for results. + # + tmpdir = os.path.join(cache_dir, "cmake") + if not os.path.exists(tmpdir): + os.makedirs(tmpdir) + else: + tmpdir = tempfile.mkdtemp() + try: + cmd = ["cmake", for_dir] + try: + p = subprocess.Popen(cmd, cwd=tmpdir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except: + logger.error(_("Unable to run {}").format(cmd)) + raise + stdout, stderr = p.communicate() + level = logging.ERROR if p.returncode else logging.DEBUG + logger.log(level, _("CMake output:\n{}").format(stdout)) + if p.returncode: + raise subprocess.CalledProcessError(p.returncode, cmd, stdout) + with open(os.path.join(tmpdir, "configure.json"), "rU",) as f: + return normalise(json.load(f)) + finally: + if not cache_dir: + shutil.rmtree(tmpdir) + + +def main(argv=None): + """ + Rules engine for SIP file generation. + + Examples: + + rules.py + """ + if argv is None: + argv = sys.argv + parser = argparse.ArgumentParser(epilog=inspect.getdoc(main), + formatter_class=HelpFormatter) + parser.add_argument("-v", "--verbose", action="store_true", default=False, help=_("Enable verbose output")) + try: + args = parser.parse_args(argv[1:]) + if args.verbose: + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s: %(message)s') + else: + logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') + # + # Generate help! + # + for db in [RuleSet, ContainerRuleDb, ForwardDeclarationRuleDb, FunctionRuleDb, ParameterRuleDb, TypedefRuleDb, + UnexposedRuleDb, VariableRuleDb, MethodCodeDb, ModuleCodeDb, TypeCodeDb]: + name = db.__name__ + print(name) + print("=" * len(name)) + print() + print(inspect.getdoc(db)) + print() + except Exception as e: + tbk = traceback.format_exc() + print(tbk) + return -1 + + +if __name__ == "__main__": + sys.exit(main()) Index: find-modules/module_generation/sip_generator.py =================================================================== --- /dev/null +++ find-modules/module_generation/sip_generator.py @@ -0,0 +1,1493 @@ +#!/usr/bin/env python +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# Copyright 2016 by Stephen Kelly (steveire@gmail.com) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +"""SIP file generator.""" + +from __future__ import print_function +import argparse +import gettext +import inspect +import logging +import os +import re +import subprocess +import sys +import traceback + +from clang.cindex import AccessSpecifier, Config, Index, SourceRange, StorageClass, TokenKind, TypeKind +import pcpp.preprocessor + +import clangcparser +import rule_helpers +import utils +from clangcparser import CursorKind +import rules_engine +from utils import item_describe, trace_discarded_by, trace_generated_for, trace_modified_by + + +class HelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter): + pass + + +logger = logging.getLogger(__name__) +gettext.install(__name__) + +# Keep PyCharm happy. +_ = _ + +EXPR_KINDS = [ + CursorKind.UNEXPOSED_EXPR, + CursorKind.CONDITIONAL_OPERATOR, CursorKind.UNARY_OPERATOR, CursorKind.BINARY_OPERATOR, + CursorKind.INTEGER_LITERAL, CursorKind.FLOATING_LITERAL, CursorKind.STRING_LITERAL, + CursorKind.CXX_BOOL_LITERAL_EXPR, CursorKind.CXX_STATIC_CAST_EXPR, CursorKind.DECL_REF_EXPR +] +TEMPLATE_KINDS = [ + CursorKind.TYPE_REF, CursorKind.TEMPLATE_REF, CursorKind.NAMESPACE_REF + ] + EXPR_KINDS +VARIABLE_KINDS = [CursorKind.VAR_DECL, CursorKind.FIELD_DECL] +FN_KINDS = [CursorKind.CXX_METHOD, CursorKind.FUNCTION_DECL, CursorKind.FUNCTION_TEMPLATE, + CursorKind.CONSTRUCTOR, CursorKind.DESTRUCTOR, CursorKind.CONVERSION_FUNCTION] +# +# All Qt-specific logic is driven from these identifiers. Setting them to +# nonsense values would effectively disable all Qt-specific logic. +# +QFLAGS = "QFlags" +Q_NULLPTR = "Q_NULLPTR" +Q_OBJECT = "Q_OBJECT" +Q_SIGNALS = "Q_SIGNALS" +Q_SLOTS = "Q_SLOTS" +Q_DECLARE_PRIVATE = "Q_DECLARE_PRIVATE" +QScopedPointer = "QScopedPointer" +# +# Function pointers are a tricky area. We need to detect them by text matching. +# +FUNC_PTR = "(*)" +# +# Function decorator keywords. +# +FN_PREFIX_INLINE = "inline " +FN_PREFIX_STATIC = "static " +FN_PREFIX_VIRTUAL = "virtual " +FN_SUFFIX_CONST = " const" +FN_SUFFIX_PURE = " = 0" + + +class SourceProcessor(pcpp.preprocessor.Preprocessor): + """ + Centralise all processing of the source. + + Ideally, we'd use Clang for everything, but on occasion, we'll need access + to the source, both with and without pre-processing too, and for that we + use pcpp.preprocessor.Preprocessor. At least by keeping all the logic here, + we try to avoid drift between the two. + """ + def __init__(self, exe_clang, compile_flags, verbose): + super(SourceProcessor, self).__init__() + self.exe_clang = exe_clang + self.compile_flags = compile_flags + self.verbose = verbose + self.source = None + self.unpreprocessed_source = [] + self.preproc = None + + def compile(self, source): + """ + Use Clang to parse the source and return its AST. + + :param source: The source file. + """ + if source != self.source: + self.unpreprocessed_source = [] + self.preproc = None + self.source = source + if self.verbose: + logger.info(" ".join(self.compile_flags + [self.source])) + tu = Index.create().parse(self.source, self.compile_flags) + # + # Stash ourselves on the tu for later use. + # + tu.source_processor = self + return tu + + def on_error(self, file, line, msg): + logger.error(msg) + self.return_code += 1 + + def on_warning(self, file, line, msg): + logger.error(msg) + + def on_directive_unknown(self, directive, toks, ifpassthru): + msg = _("Unknown directive '{}'").format("".join(tok.value for tok in toks)) + if directive.value == "warning": + self.on_warning(directive.source, directive.lineno, msg) + else: + self.on_error(directive.source, directive.lineno, msg) + return True + + def on_include_not_found(self, is_system_include, curdir, includepath): + msg = _("Include file '{}' not found").format(includepath) + self.on_error(self.lastdirective.source, self.lastdirective.lineno, msg) + + def unpreprocessed(self, extent, nl=" "): + """ + Read the given range from the raw source. + + :param extent: The range of text required. + """ + assert self.source, "Must call compile() first!" + if not self.unpreprocessed_source: + self.unpreprocessed_source = self._read(self.source) + text = self._extract(self.unpreprocessed_source, extent) + if nl != "\n": + text = text.replace("\n", nl) + return text + + def preprocessed(self, extent, alternate_source=None, nl=" "): + """ + Read the given range from the pre-processed source. + + :param extent: The range of text required. + """ + if alternate_source: + lines = self._read(alternate_source) + text = self._extract(lines, extent) + else: + text = self.unpreprocessed(extent, nl="\n") + return self.expand(text, nl=nl) + + def expand(self, text, nl=" "): + assert self.source, "Must call compile() first!" + if not self.preproc: + # + # Clang cannot do -fsyntax-only...get the macros by hand. + # + cmd = [self.exe_clang] + self.compile_flags + ["-dM", "-E"] + [self.source] + if self.verbose: + logger.info(" ".join(cmd)) + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + stdout, stderr = p.communicate() + if stderr: + logger.error(_("While expanding '{}':\n{})".format(text, stderr))) + self.preproc = pcpp.preprocessor.Preprocessor() + self.preproc.parser = self.preproc.parsegen(stdout) + # + # This is what takes the most time, not Clang preprocessing above! + # + self.preproc.write() + # + # Tokenize the input text (and then fixup the needed token.source). + # + tokens = self.preproc.tokenize(text) + for t in tokens: + t.source = self.source + # + # Now the actual expansion. + # + tokens = self.preproc.expand_macros(tokens) + text = "".join([t.value for t in tokens]) + if nl != "\n": + text = text.replace("\n", nl) + return text + + def _read(self, source): + lines = [] + with open(source, "rU") as f: + for line in f: + lines.append(line) + return lines + + def _extract(self, lines, extent): + extract = lines[extent.start.line - 1:extent.end.line] + if extent.start.line == extent.end.line: + extract[0] = extract[0][extent.start.column - 1:extent.end.column - 1] + else: + extract[0] = extract[0][extent.start.column - 1:] + extract[-1] = extract[-1][:extent.end.column - 1] + # + # Return a single buffer of text. + # + return "".join(extract) + + +class TemplatingStack(list): + """ + A stack of sets of templated objects. + """ + def parameters_fixup(self, sip, key): + """ + Clang seems to replace template parameter N of the form "T" with + "type-parameter--N"...so we need to put "T" back. + + :param sip: The sip. + :param key: The key in the sip which may need + fixing up. + :return: + """ + for depth, templated_object in enumerate(self): + template_parameters = templated_object.template_parameters + for clang_parameter, real_parameter in enumerate(template_parameters): + clang_parameter = "type-parameter-{}-{}".format(depth, clang_parameter) + # + # Depending on the type of the SIP entry, replace the Clang + # version of the value with the actual version. + # + assert not isinstance(real_parameter, tuple) + value = sip[key] + if isinstance(value, str): + sip[key] = value.replace(clang_parameter, real_parameter) + elif isinstance(value, list): + for j, item in enumerate(value): + sip[key][j] = item.replace(clang_parameter, real_parameter) + elif isinstance(value, dict): + for j, item in value.items(): + sip[key][j] = item.replace(clang_parameter, real_parameter) + + def push_first(self, templated_object, new_parameter): + """ + Push a new level onto the stack, and add a new parameter. + """ + if not self or self[-1] is not templated_object: + self.append(templated_object) + if templated_object.template_parameters is None: + templated_object.template_parameters = [] + templated_object.template_parameters.append(new_parameter) + + def pop_last(self, templated_object): + """ + Pop an old level off the stack as needed. + """ + if self and self[-1] is templated_object: + self.pop() + + +class SipGenerator(object): + def __init__(self, exe_clang, rules_pkg, compile_flags, dump_modules=False, dump_items=False, dump_includes=False, + dump_privates=False, verbose=False): + """ + Constructor. + + :param exe_clang: The Clang compiler. + :param rules_pkg: The rules for the file. + :param compile_flags: The compile flags for the file. + :param dump_modules: Turn on tracing for modules. + :param dump_items: Turn on tracing for container members. + :param dump_includes: Turn on diagnostics for include files. + :param dump_privates: Turn on diagnostics for omitted private items. + :param verbose: Turn on diagnostics for command lines. + """ + self.exe_clang = exe_clang + self.compiled_rules = rules_engine.rules(rules_pkg) + self.compile_flags = compile_flags + self.dump_modules = dump_modules + self.dump_items = dump_items + self.dump_includes = dump_includes + self.dump_privates = dump_privates + self.verbose = verbose + self.diagnostics = set() + self.tu = None + self.source_processor = None + + def create_sip(self, h_file, include_filename): + """ + Actually convert the given source header file into its SIP equivalent. + This is the main entry point for this class. + + :param h_file: The source header file of interest. + :param include_filename: The short header to include in the sip file. + :returns: A (body, modulecode, includes). The body is the SIP text + corresponding to the h_file, it can be a null string indicating + there was nothing that could be generated. The modulecode is + a dictionary of fragments of code that are to be emitted at + module scope. The includes is a iterator over the files which + clang #included while processing h_file. + """ + # + # Read in the original file. + # + source = h_file + self.source_processor = SourceProcessor(self.exe_clang, ["-x", "c++"] + self.compile_flags, self.verbose) + tu = self.source_processor.compile(source) + self.tu = clangcparser.TranslationUnitCursor(tu.cursor) + for diag in self.tu.diagnostics: + # + # We expect to be run over hundreds of files. Any parsing issues are likely to be very repetitive. + # So, to avoid bothering the user, we suppress duplicates. + # + loc = diag.location + msg = "{}:{}[{}] {}".format(loc.file, loc.line, loc.column, diag.spelling) + if diag.spelling == "#pragma once in main file": + continue + if msg in self.diagnostics: + continue + self.diagnostics.add(msg) + logger.log(diag.severity, "While parsing: {}".format(msg)) + if self.dump_includes: + logger.info(_("File {} (include {})").format(h_file, include_filename)) + for include in sorted(set(self.tu.get_includes())): + logger.info(_(" #includes {}").format(include.include.name)) + # + # Run through the top level children in the translation unit. + # + body, modulecode = self._container_get(self.tu, -1, h_file, include_filename, TemplatingStack()) + if body: + # + # Any module-related manual code (%ExportedHeaderCode, %ModuleCode, %ModuleHeaderCode or other + # module-level directives? + # + h_name = os.path.basename(h_file) + sip = { + "name": os.path.basename(include_filename), + "decl": body + } + body = "" + if self.dump_modules: + logger.info(_("Processing module for {}").format(include_filename)) + modifying_rule = self.compiled_rules.modulecode(include_filename, sip) + if sip["name"]: + if modifying_rule: + body += "// Modified {} (by {}):\n".format(include_filename, modifying_rule) + body += sip["decl"] + sip["code"] + # + # Support any global externs. + # + body = """ +%ModuleHeaderCode +#include <{}> +%End\n""".format(include_filename) + body + else: + body = "// Discarded {} (by {}):\n".format(h_name, modifying_rule) + return body, modulecode, self.tu.get_includes + + def skippable_attribute(self, parent, member, text, sip): + """ + We don't seem to have access to the __attribute__(())s, but at least we can look for stuff we care about. + + :param parent: Parent object. + :param member: The attribute. + :param text: The raw source corresponding to the region of member. + :param sip: the sip. + """ + if member.kind == CursorKind.UNEXPOSED_ATTR and text.find("_DEPRECATED") != -1: + sip["annotations"].add("Deprecated") + return True + if member.kind != CursorKind.VISIBILITY_ATTR: + return False + if member.spelling == "hidden": + if self.dump_privates: + SipGenerator._report_ignoring(parent, "hidden") + sip["name"] = "" + return True + return False + + def _container_get(self, container, level, h_file, include_filename, templating_stack): + """ + Generate the (recursive) translation for a class or namespace. + + :param container: A class or namespace. + :param level: Recursion level controls indentation. + :param h_file: The source header file of interest. + :param include_filename: The short header to include in the sip file. + :param templating_stack: The stack of sets of template parameters. + :return: A string. + """ + + def in_class(item): + parent = item.semantic_parent + while parent and parent.kind != CursorKind.CLASS_DECL: + parent = parent.semantic_parent + return True if parent else False + + sip = { + "name": container.spelling, + "annotations": set() + } + body = "" + base_specifiers = [] + had_copy_constructor = None + need_private_copy_constructor = False + had_assignment_operator = None + modulecode = {} + is_signal = False + for member in container.get_children(): + # + # Only emit items in the translation unit. + # + if member.location.file.name != self.tu.spelling: + continue + # + # Skip almost anything which is private. + # + if member.access_specifier == AccessSpecifier.PRIVATE: + # + # We need to see: + # + # - Any existing constructors (no-copy constructor support). + # - Any destructors and virtuals (for SIP as per + # https://www.riverbankcomputing.com/pipermail/pyqt/2017-March/038944.html). + # - VARIABLE_KINDS to see any const variables (no-copy constructor support). + # - CursorKind.CXX_ACCESS_SPEC_DECL so that changes in visibility are seen. + # - CursorKind.USING_DECLARATION for any functions being access-tweaked. + # + if (member.kind == CursorKind.CONSTRUCTOR and (member.is_converting_constructor() or + member.is_copy_constructor() or + member.is_default_constructor() or + member.is_move_constructor())) or \ + (member.kind == CursorKind.DESTRUCTOR) or \ + (member.kind in FN_KINDS and member.is_virtual_method()) or \ + (member.kind in VARIABLE_KINDS + [CursorKind.CXX_ACCESS_SPEC_DECL, CursorKind.USING_DECLARATION]): + pass + else: + if self.dump_privates: + SipGenerator._report_ignoring(member, "private") + continue + decl = "" + if member.kind in FN_KINDS: + # + # Abstract? + # + if member.is_pure_virtual_method(): + sip["annotations"].add("Abstract") + elif member.is_copy_constructor(): + if self.source_processor.preprocessed(member.extent).endswith("= delete;"): + need_private_copy_constructor = True + continue + else: + had_copy_constructor = member + elif member.spelling == "operator=": + had_assignment_operator = member + decl, tmp = self._fn_get(container, member, level + 1, is_signal, templating_stack) + modulecode.update(tmp) + elif member.kind == CursorKind.ENUM_DECL: + decl, tmp = self._enum_get(container, member, level + 1) + modulecode.update(tmp) + elif member.kind == CursorKind.CXX_ACCESS_SPEC_DECL: + decl, is_signal = self._get_access_specifier(member, level + 1) + elif member.kind == CursorKind.TYPEDEF_DECL: + # + # Typedefs for inlined enums/structs/unions seem to be emitted twice. Refer back to original. + # There should be only one child... + # + typedef_children = list(member.get_children()) + if len(typedef_children) == 1 and typedef_children[0].kind in [CursorKind.ENUM_DECL, + CursorKind.STRUCT_DECL, + CursorKind.UNION_DECL]: + child = typedef_children[0] + if not child.displayname: + child_type = child.SIP_TYPE_NAME + original = child_type + " " + child.spelling + typedef = child_type + " " + member.type.spelling + body = body.replace(original, typedef, 1) + else: + decl, tmp = self._typedef_get(container, member, level + 1, h_file, include_filename, + templating_stack) + modulecode.update(tmp) + elif member.kind == CursorKind.CXX_BASE_SPECIFIER: + # + # SIP does not want protected or private base specifiers... + # + if member.access_specifier == AccessSpecifier.PUBLIC: + base_specifiers.append(member.type.get_canonical().spelling) + elif isinstance(member, clangcparser.TemplateParameterCursor): + templating_stack.push_first(container, member.SIP_TYPE_NAME) + elif isinstance(member, clangcparser.VariableCursor): + if member.access_specifier != AccessSpecifier.PRIVATE: + decl, tmp = self._var_get(container, member, level + 1) + modulecode.update(tmp) + else: + if member.type.is_const_qualified() or member.type.spelling.startswith(QScopedPointer): + need_private_copy_constructor = True + if self.dump_privates: + SipGenerator._report_ignoring(member, "private") + elif member.kind in [CursorKind.NAMESPACE, CursorKind.CLASS_DECL, + CursorKind.CLASS_TEMPLATE, CursorKind.CLASS_TEMPLATE_PARTIAL_SPECIALIZATION, + CursorKind.STRUCT_DECL, CursorKind.UNION_DECL]: + decl, tmp = self._container_get(member, level + 1, h_file, include_filename, templating_stack) + modulecode.update(tmp) + elif member.kind in TEMPLATE_KINDS + [CursorKind.USING_DIRECTIVE, + CursorKind.CXX_FINAL_ATTR]: + # + # Ignore: + # + # TEMPLATE_KINDS: Template type parameter. + # CursorKind.USING_DIRECTIVE: Using? Pah! + # CursorKind.CXX_FINAL_ATTR: Again, not much to be done with this. + # + pass + elif member.kind == CursorKind.USING_DECLARATION and in_class(member): + # + # If we are not in a class, a USING_DECLARATION cannot be modifying access levels. + # + decl, tmp = self._using_get(container, member, level + 1) + modulecode.update(tmp) + else: + text = self.source_processor.unpreprocessed(member.extent) + if self.skippable_attribute(container, member, text, sip): + if not sip["name"]: + templating_stack.pop_last(container) + return "", modulecode + elif member.kind == CursorKind.UNEXPOSED_DECL: + if text.startswith(Q_DECLARE_PRIVATE + "("): + need_private_copy_constructor = True + decl, tmp = self._unexposed_get(container, member, text, level + 1) + modulecode.update(tmp) + else: + SipGenerator._report_ignoring(member, "unusable") + if self.dump_items: + logger.info(_("Processing {}").format(item_describe(member))) + body += "// Processing {}\n".format(item_describe(member)) + if decl: + body += decl + + if isinstance(container, clangcparser.TranslationUnitCursor): + templating_stack.pop_last(container) + return body, modulecode + + sip["decl"] = container.SIP_TYPE_NAME + " " + sip["name"] + sip["template_parameters"] = container.template_parameters + sip["base_specifiers"] = base_specifiers + + pad = " " * (level * 4) + # + # Empty containers are still useful if they provide namespaces, classes or forward declarations. + # + if not body: + text = self.source_processor.unpreprocessed(container.extent) + if not text.endswith("}"): + # + # Forward declaration. + # + modifying_rule = self.compiled_rules.forward_declaration_rules().apply(container, sip) + if sip["name"]: + if modifying_rule: + body += trace_modified_by(container, modifying_rule) + body += pad + sip["decl"] + if sip["annotations"]: + body += " /" + ",".join(sip["annotations"]) + "/" + body += ";\n" + else: + body = pad + trace_discarded_by(container, modifying_rule) + templating_stack.pop_last(container) + return body, modulecode + else: + # + # Empty body provides a namespace or no-op subclass. + # + body = pad + " // Empty!\n" + # + # Flesh out the SIP context for the rules engine. + # + sip["body"] = body + templating_stack.parameters_fixup(sip, "body") + templating_stack.parameters_fixup(sip, "base_specifiers") + modifying_rule = self.compiled_rules.container_rules().apply(container, sip) + if sip["name"]: + decl = "" + if modifying_rule: + templating_stack.parameters_fixup(sip, "body") + templating_stack.parameters_fixup(sip, "base_specifiers") + decl += pad + trace_modified_by(container, modifying_rule) + # + # Any type-related code (%BIGetBufferCode, %BIGetReadBufferCode, %BIGetWriteBufferCode, + # %BIGetSegCountCode, %BIGetCharBufferCode, %BIReleaseBufferCode, %ConvertToSubClassCode, + # %ConvertToTypeCode, %GCClearCode, %GCTraverseCode, %InstanceCode, %PickleCode, %TypeCode, + # %TypeHeaderCode other type-related directives)? + # + modifying_rule = self.compiled_rules.typecode(container, sip) + if modifying_rule: + templating_stack.parameters_fixup(sip, "body") + templating_stack.parameters_fixup(sip, "base_specifiers") + decl += pad + trace_modified_by(container, modifying_rule) + decl += pad + sip["decl"] + if sip["base_specifiers"]: + decl += ": " + ", ".join(sip["base_specifiers"]) + if sip["annotations"]: + decl += " /" + ",".join(sip["annotations"]) + "/" + if sip["template_parameters"]: + decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl + decl += "\n" + pad + "{\n" + decl += "%TypeHeaderCode\n#include <{}>\n%End\n".format(include_filename) + if isinstance(container, clangcparser.ContainerCursor) and container.initial_access_specifier: + decl += pad + container.initial_access_specifier + "\n" + decl += sip["code"] + body = decl + sip["body"] + if container.kind != CursorKind.NAMESPACE and not sip["decl"].startswith("%Exception"): + # + # Generate private copy constructor for non-copyable types. + # + if need_private_copy_constructor and not had_copy_constructor: + body += pad + "private:\n" + body += pad + " " + trace_generated_for(container, "non-copyable type handling", {}) + body += pad + " {}(const {} &);\n".format(sip["name"], sip["name"]) + # + # Generate private assignment operator for non-assignable types. + # + if had_assignment_operator: + body += pad + "private:\n" + body += pad + " " + trace_generated_for(container, "non-assignable type handling", {}) + body += pad + " {} &operator=(const {} &);\n".format(sip["name"], sip["name"]) + body += pad + "};\n" + if sip["modulecode"]: + modulecode.update(sip["modulecode"]) + else: + body = pad + trace_discarded_by(container, modifying_rule) + templating_stack.pop_last(container) + return body, modulecode + + def _get_access_specifier(self, member, level): + """ + In principle, we just want member.access_specifier.name.lower(), except that we need to handle: + + Q_OBJECT + Q_SIGNALS:|signals: + public|private|protected Q_SLOTS:|slots: + + which are converted by the preprocessor...so read the original text. + + :param member: The access_specifier. + :return: + """ + access_specifier = "" + is_signal = False + access_specifier_text = self.source_processor.unpreprocessed(member.extent) + if access_specifier_text == Q_OBJECT: + return access_specifier, is_signal + pad = " " * ((level - 1) * 4) + if access_specifier_text in (Q_SIGNALS + ":", "signals:"): + access_specifier = access_specifier_text + is_signal = True + elif access_specifier_text in ("public " + Q_SLOTS + ":", "public slots:", "protected " + Q_SLOTS + ":", + "protected slots:"): + access_specifier = access_specifier_text + elif member.access_specifier == AccessSpecifier.PRIVATE: + access_specifier = "private:" + elif member.access_specifier == AccessSpecifier.PROTECTED: + access_specifier = "protected:" + elif member.access_specifier == AccessSpecifier.PUBLIC: + access_specifier = "public:" + else: + access_specifier = "public: // Mapped from " + access_specifier_text + logger.warn(_("// Replaced '{}' with 'public' (by {})".format(access_specifier_text, + "access specifier handling"))) + decl = pad + access_specifier + "\n" + return decl, is_signal + + def _enum_get(self, container, enum, level): + sip = { + "name": enum.spelling, + "annotations": set(), + } + modulecode = {} + enumerations = [] + for enumeration in enum.get_children(): + # + # Skip visibility attributes and the like. + # + if enumeration.kind == CursorKind.ENUM_CONSTANT_DECL: + enumerations.append(enumeration.displayname) + else: + SipGenerator._report_ignoring(enumeration, "unusable") + sip["decl"] = "enum " + sip["name"] + sip["enumerations"] = enumerations + modifying_rule = self.compiled_rules.variable_rules().apply(container, enum, sip) + pad = " " * (level * 4) + if sip["name"]: + decl = "" + if modifying_rule: + decl += pad + trace_modified_by(enum, modifying_rule) + decl += pad + sip["decl"] + "\n" + decl += pad + "{\n" + decl += ",\n".join([pad + " " + e for e in sip["enumerations"]]) + "\n" + decl += pad + "}" + if sip["annotations"]: + decl += " /" + ",".join(sip["annotations"]) + "/" + decl = decl + sip["code"] + ";\n" + if sip["modulecode"]: + modulecode.update(sip["modulecode"]) + else: + decl = pad + trace_discarded_by(enum, modifying_rule) + return decl, modulecode + + def _fn_get(self, container, fn, level, is_signal, templating_stack): + """ + Generate the translation for a function. + + :param container: A class or namespace. + :param fn: The function object. + :param level: Recursion level controls indentation. + :param is_signal: Is this a Qt signal? + :param templating_stack: The stack of sets of template parameters. + :return: A string. + """ + # + # Discard inline implementations of functions declared in a class/struct. + # + if fn.is_implementation(container): + SipGenerator._report_ignoring(fn, "inline method") + return "", {} + + sip = { + "name": fn.spelling, + "annotations": set(), + "is_signal": is_signal, + } + # + # Constructors for templated classes end up with spurious template parameters. + # + if fn.kind == CursorKind.CONSTRUCTOR: + sip["name"] = sip["name"].split("<")[0] + parameters = [] + parameter_modifying_rules = [] + modulecode = {} + for child in fn.get_children(): + if child.kind == CursorKind.PARM_DECL: + # + # So far so good, but we need any default value. + # + decl = child.SIP_TYPE_NAME + # + # SIP does not support "const char *const foo". + # + decl = decl.replace("*const " + child.spelling, "*" + child.spelling) + child_sip = { + "name": child.spelling, + "decl": decl, + "init": self._fn_get_parameter_default(fn, child), + "annotations": set() + } + templating_stack.parameters_fixup(child_sip, "decl") + modifying_rule = self.compiled_rules.parameter_rules().apply(container, fn, child, child_sip) + if modifying_rule: + templating_stack.parameters_fixup(child_sip, "decl") + parameter_modifying_rules.append(trace_modified_by(child, modifying_rule)) + decl = child_sip["decl"] + if child_sip["annotations"]: + decl += " /" + ",".join(child_sip["annotations"]) + "/" + if child_sip["init"]: + decl += " = " + child_sip["init"] + if child_sip["modulecode"]: + templating_stack.parameters_fixup(child_sip, "modulecode") + modulecode.update(child_sip["modulecode"]) + parameters.append(decl) + elif child.kind in [CursorKind.COMPOUND_STMT, CursorKind.CXX_OVERRIDE_ATTR, + CursorKind.MEMBER_REF, CursorKind.DECL_REF_EXPR, CursorKind.CALL_EXPR] + TEMPLATE_KINDS: + # + # Ignore: + # + # CursorKind.COMPOUND_STMT: Function body. + # CursorKind.CXX_OVERRIDE_ATTR: The "override" keyword. + # CursorKind.MEMBER_REF, CursorKind.DECL_REF_EXPR, CursorKind.CALL_EXPR: Constructor initialisers. + # TEMPLATE_KINDS: The result type. + # + pass + elif isinstance(child, clangcparser.TemplateParameterCursor): + templating_stack.push_first(fn, child.SIP_TYPE_NAME) + else: + text = self.source_processor.unpreprocessed(child.extent) + if self.skippable_attribute(fn, child, text, sip): + if not sip["name"]: + templating_stack.pop_last(fn) + return "", modulecode + else: + SipGenerator._report_ignoring(child, "unusable") + # + # Flesh out the SIP context for the rules engine. + # + sip["template_parameters"] = fn.template_parameters + if fn.kind in [CursorKind.CONSTRUCTOR, CursorKind.DESTRUCTOR]: + sip["fn_result"] = "" + else: + # + # If the function returns a function, emit the non-lowered type to + # maximise the probability that SIP can handle the output. + # + if fn.result_type.get_canonical().is_a_function: + sip["fn_result"] = fn.result_type.spelling + else: + sip["fn_result"] = fn.result_type.get_canonical().spelling + sip["parameters"] = parameters + sip["prefix"], sip["suffix"] = self._fn_get_decorators(container, fn) + templating_stack.parameters_fixup(sip, "fn_result") + templating_stack.parameters_fixup(sip, "parameters") + modifying_rule = self.compiled_rules.function_rules().apply(container, fn, sip) + pad = " " * (level * 4) + if sip["name"]: + decl1 = "" + if modifying_rule: + templating_stack.parameters_fixup(sip, "fn_result") + templating_stack.parameters_fixup(sip, "parameters") + decl1 += pad + trace_modified_by(fn, modifying_rule) + for modifying_rule in parameter_modifying_rules: + decl1 += pad + modifying_rule + decl = "" + # + # Any method-related code (%MethodCode, %VirtualCatcherCode, VirtualCallCode + # or other method-related directives)? + # + modifying_rule = self.compiled_rules.methodcode(fn, sip) + if modifying_rule: + templating_stack.parameters_fixup(sip, "fn_result") + templating_stack.parameters_fixup(sip, "parameters") + decl1 += pad + trace_modified_by(fn, modifying_rule) + decl += self._function_render(fn, sip, pad) + decl = decl1 + decl + if sip["modulecode"]: + templating_stack.parameters_fixup(sip, "modulecode") + modulecode.update(sip["modulecode"]) + else: + decl = pad + trace_discarded_by(fn, modifying_rule) + templating_stack.pop_last(fn) + return decl, modulecode + + def _function_render(self, fn, sip, pad): + """ + Render a function as output text. + """ + decl = sip["name"] + "(" + ", ".join(sip["parameters"]) + ")" + if sip["fn_result"]: + if sip["fn_result"][-1] in "*&": + decl = sip["fn_result"] + decl + else: + decl = sip["fn_result"] + " " + decl + # + # Note that we never emit any "inline" prefix: it is only there to help rule-writers. + # + prefix = sip["prefix"].replace(FN_PREFIX_INLINE, "") + decl = pad + prefix + decl + sip["suffix"] + if sip["annotations"]: + decl += " /" + ",".join(sip["annotations"]) + "/" + if sip["template_parameters"]: + decl = pad + "template <" + ", ".join(sip["template_parameters"]) + ">\n" + decl + if sip["cxx_parameters"] or sip["cxx_fn_result"]: + if not isinstance(sip["cxx_parameters"], str): + sip["cxx_parameters"] = ", ".join(sip["cxx_parameters"]) + decl += "\n " + pad + "[" + # + # SIP does not want the result for constructors. + # + if fn.kind != CursorKind.CONSTRUCTOR: + if sip["cxx_fn_result"][-1] in "*&": + decl += sip["cxx_fn_result"] + else: + decl += sip["cxx_fn_result"] + " " + decl += "(" + sip["cxx_parameters"] + ")]" + decl += ";\n" + decl += sip["code"] + return decl + + def _fn_get_decorators(self, container, fn): + """ + The parser does not provide direct access to the complete keywords (explicit, const, static, etc) of a function + in the displayname. It would be nice to get these from the AST, but I cannot find where they are hiding. + + Now, we could resort to using the original source. That does not bode well if you have macros (QOBJECT, + xxxDEPRECATED?), inlined bodies and the like, using the rule engine could be used to patch corner cases... + + ...or we can try to guess what SIP cares about, i.e static and maybe const. Luckily (?), we have those to hand! + + :param fn: The function object. + :return: prefix, suffix String containing any prefix or suffix keywords. + """ + suffix = "" + if fn.is_const_method(): + suffix += FN_SUFFIX_CONST + prefix = "" + if fn.is_definition(): + # + # The support for "inline" is for the benefit of rule-writers who might, for example, need to suppress + # *any* definition, not necessarily one that the user marked as "inline". It is never emitted. + # + prefix += FN_PREFIX_INLINE + # + # A namespace cannot have "virtual" or "static". + # + if container.kind != CursorKind.NAMESPACE: + if fn.is_static_method(): + prefix += FN_PREFIX_STATIC + if fn.is_virtual_method(): + prefix += FN_PREFIX_VIRTUAL + if fn.is_pure_virtual_method(): + suffix += FN_SUFFIX_PURE + return prefix, suffix + + QUALIFIED_ID = re.compile("(?:[a-z_][a-z_0-9]*::)*([a-z_][a-z_0-9]*)", re.I) + + def _fn_get_parameter_default(self, function, parameter): + """ + The parser does not seem to provide access to the complete text of a parameter. + This makes it hard to find any default values, so we: + + 1. Run the lexer from "here" to the end of the file, bailing out when we see the "," + or a ")" marking the end. + 2. Watch for the assignment. + """ + def decompose_arg(arg, spellings): + template, args = utils.decompose_template(arg) + spellings.append(template) + if args is not None: + for arg in args: + decompose_arg(arg, spellings) + + def _get_param_type(parameter): + q_flags_enum = None + canonical = underlying = parameter.type + spellings = [] + while underlying: + prefixes, name, operators, suffixes, next = underlying.decomposition() + name, args = utils.decompose_template(name) + # + # We want the name (or name part of the template), plus any template parameters to deal with: + # + # QList &constraints = QList() + # + spellings.append(name) + if args is not None: + for arg in args: + decompose_arg(arg, spellings) + if name == QFLAGS: + # + # The name of the enum. + # + q_flags_enum = args[0] + return spellings, q_flags_enum, underlying + canonical = underlying + underlying = next + return spellings, q_flags_enum, canonical.get_canonical() + + def _get_param_value(text, parameter): + + def mangler_enum(spellings, rhs, fqn): + # + # Is rhs the suffix of any of the typedefs? + # + for spelling in spellings[:-1]: + name, args = utils.decompose_template(spelling) + if name.endswith(rhs): + return name + prefix = spellings[-1].rsplit("::", 1)[0] + "::" + return prefix + rhs + + def mangler_other(spellings, rhs, fqn): + # + # Is rhs the suffix of any of the typedefs? + # + for spelling in spellings: + name, args = utils.decompose_template(spelling) + if name.endswith(rhs): + return name + return fqn + + if text in ["", "0", "nullptr", Q_NULLPTR]: + return text + spellings, q_flags_enum, canonical_t = _get_param_type(parameter) + if text == "{}": + if q_flags_enum or canonical_t.kind == TypeKind.ENUM: + return "0" + if canonical_t.kind == TypeKind.POINTER: + return "nullptr" + # + # TODO: return the lowest or highest type? + # + return spellings[-1] + "()" + # + # SIP wants things fully qualified. Without creating a full AST, we can only hope to cover the common + # cases: + # + # - Enums may come as a single value or an or'd list: + # + # Input Output + # ----- ------ + # Option1 parents::Option1 + # Flag1|Flag3 parents::Flag1|parents::Flag3 + # FlagsType(Flag1|Flag3) parents::FlagsType(parents::Flag1|parents::Flag3) + # LookUpMode(exactOnly) | defaultOnly + # parents::LookUpMode(parents::exactOnly) | parents::defaultOnly + # + # So, prefix any identifier with the prefix of the enum. + # + # - For other cases, if any (qualified) id in the default value matches the RHS of the parameter + # type, use the parameter type. + # + if q_flags_enum or canonical_t.kind == TypeKind.ENUM: + mangler = mangler_enum + else: + mangler = mangler_other + tmp = "" + match = SipGenerator.QUALIFIED_ID.search(text) + while match: + tmp += match.string[:match.start()] + rhs = match.group(1) + fqn = match.group() + tmp += mangler(spellings, rhs, fqn) + text = text[match.end():] + match = SipGenerator.QUALIFIED_ID.search(text) + tmp += text + return tmp + + for member in parameter.get_children(): + if member.kind.is_expression(): + # + # Get the text after the "=". Macro expansion can make relying on tokens fraught...and + # member.get_tokens() simply does not always return anything. + # + possible_extent = SourceRange.from_locations(parameter.extent.start, function.extent.end) + text = "" + bracket_level = 0 + found_start = False + found_end = False + was_punctuated = True + for token in self.tu.get_tokens(extent=possible_extent): + # + # Now count balanced anything-which-can-contain-a-comma till we get to the end. + # + if bracket_level == 0 and token.spelling == "=" and not found_start: + found_start = True + elif bracket_level == 0 and token.spelling in ",)": + found_end = True + text = text[1:] + break + elif token.spelling in "<(": + bracket_level += 1 + elif token.spelling in ")>": + bracket_level -= 1 + if found_start: + if (token.kind != TokenKind.PUNCTUATION and not was_punctuated) or (token.spelling in "*&"): + text += " " + text += token.spelling + was_punctuated = token.kind == TokenKind.PUNCTUATION + if not found_end and text: + raise RuntimeError(_("No end found for {}::{}, '{}'").format(function.spelling, parameter.spelling, + text)) + # + # SIP does not like outer brackets as in "(QHash())". Get rid of them. + # + while text.startswith("("): + text = text[1:-1] + # + # Use some heuristics to format the default value as SIP wants in most cases. + # + return _get_param_value(text, parameter) + return "" + + def _typedef_get(self, container, typedef, level, h_file, include_filename, templating_stack): + """ + Generate the translation for a typedef. + + :param container: A class or namespace. + :param typedef: The typedef object. + :param level: Recursion level controls indentation. + :param h_file: The source header file of interest. + :param include_filename: The short header to include in the sip file. + :param templating_stack: The stack of sets of template parameters. + :return: A string. + """ + sip = { + "name": typedef.displayname, + "annotations": set() + } + modulecode = {} + for child in typedef.get_children(): + if child.kind in [CursorKind.STRUCT_DECL, CursorKind.UNION_DECL] and not child.underlying_type: + decl, tmp = self._container_get(child, level, h_file, include_filename, templating_stack) + modulecode.update(tmp) + else: + text = self.source_processor.unpreprocessed(child.extent) + if self.skippable_attribute(typedef, child, text, sip): + if not sip["name"]: + return "", modulecode + else: + SipGenerator._report_ignoring(child, "unusable") + # + # Flesh out the SIP context for the rules engine. + # + sip["decl"] = typedef.SIP_TYPE_NAME + # + # If the typedef is for a function type, emit the non-lowered type to + # maximise the probability that SIP can handle the output. + # + if typedef.underlying_type.get_canonical().is_a_function: + sip["fn_result"] = typedef.underlying_type.get_canonical().result_type.spelling + else: + sip["fn_result"] = "" + templating_stack.parameters_fixup(sip, "decl") + modifying_rule = self.compiled_rules.typedef_rules().apply(container, typedef, sip) + # + # Now the rules have run, add any prefix/suffix. + # + pad = " " * (level * 4) + if sip["name"]: + decl = "" + if modifying_rule: + templating_stack.parameters_fixup(sip, "decl") + decl += pad + trace_modified_by(typedef, modifying_rule) + # + # Any type-related code (%BIGetBufferCode, %BIGetReadBufferCode, %BIGetWriteBufferCode, + # %BIGetSegCountCode, %BIGetCharBufferCode, %BIReleaseBufferCode, %ConvertToSubClassCode, + # %ConvertToTypeCode, %GCClearCode, %GCTraverseCode, %InstanceCode, %PickleCode, %TypeCode + # or %TypeHeaderCode)? + # + modifying_rule = self.compiled_rules.typecode(typedef, sip) + if modifying_rule: + templating_stack.parameters_fixup(sip, "decl") + decl += pad + trace_modified_by(typedef, modifying_rule) + if sip["fn_result"]: + decl += pad + "typedef {} (*{})({})".format(sip["fn_result"], sip["name"], sip["decl"]) + decl = decl.replace("* ", "*").replace("& ", "&") + elif typedef.underlying_type.kind == TypeKind.DEPENDENTSIZEDARRAY: + decl += pad + "typedef {}".format(sip["decl"]) + else: + decl += pad + "typedef {} {}".format(sip["decl"], sip["name"]) + # + # SIP does not support deprecation of typedefs. + # + sip["annotations"].discard("Deprecated") + if sip["annotations"]: + decl += " /" + ",".join(sip["annotations"]) + "/" + decl += sip["code"] + ";\n" + if sip["modulecode"]: + templating_stack.parameters_fixup(sip, "modulecode") + modulecode.update(sip["modulecode"]) + else: + decl = pad + trace_discarded_by(typedef, modifying_rule) + return decl, modulecode + + def _unexposed_get(self, container, unexposed, text, level): + """ + The parser does not seem to provide access to the complete text of an unexposed decl. + + 1. Run the lexer from "here" to the end of the outer scope, bailing out when we see the ";" + or a "{" marking the end. + """ + sip = { + "name": unexposed.displayname, + "annotations": set() + } + # + # Flesh out the SIP context for the rules engine. NOTE: Typically, the sip["name"] for an unexposed item will + # be "", and thus trigger the discard logic (unless there is a rule in place to set the sip["name"]!). + # + sip["decl"] = text + modulecode = {} + modifying_rule = self.compiled_rules.unexposed_rules().apply(container, unexposed, sip) + # + # Now the rules have run, add any prefix/suffix. + # + pad = " " * (level * 4) + if sip["name"]: + decl = "" + if modifying_rule: + item = item_describe(unexposed, " ".join(text.split(None, 3)[:3])) + decl += pad + trace_modified_by(item, modifying_rule) + decl += pad + sip["decl"] + "\n" + if sip["modulecode"]: + modulecode.update(sip["modulecode"]) + else: + if not modifying_rule: + modifying_rule = "default unexposed handling" + item = item_describe(unexposed, " ".join(text.split(None, 3)[:3])) + decl = pad + trace_discarded_by(item, modifying_rule) + return decl, modulecode + + def _using_get(self, container, using, level): + """ + SIP does not support using declarations, so rule-writers will generally + have to intervene. + """ + sip = { + "name": using.spelling, + "annotations": set(), + } + modulecode = {} + # + # Is this for a function or a variable? + # + is_function = False + using_class = None + for child in using.get_children(): + if child.kind == CursorKind.OVERLOADED_DECL_REF: + is_function = True + elif child.kind == CursorKind.TYPE_REF: + using_class = child.spelling.split()[-1] + if is_function: + sip["template_parameters"] = [] + sip["fn_result"] = "void" + sip["parameters"] = [] + sip["prefix"], sip["suffix"] = "", "" + modifying_rule = self.compiled_rules.function_rules().apply(container, using, sip) + else: + sip["decl"] = "" + modifying_rule = self.compiled_rules.variable_rules().apply(container, using, sip) + # + # Make it clear that we intervened. + # + if not modifying_rule: + modifying_rule = "default using handling" + pad = " " * (level * 4) + if sip["name"]: + decl = "" + if modifying_rule: + item = item_describe(using, using.spelling + " -> " + using_class) + decl += pad + trace_modified_by(item, modifying_rule) + if is_function: + decl += self._function_render(using, sip, pad) + else: + decl += self._var_render(using, sip, pad) + if sip["modulecode"]: + modulecode.update(sip["modulecode"]) + else: + item = item_describe(using, using.spelling + " -> " + using_class) + decl = pad + trace_discarded_by(item, modifying_rule) + return decl, modulecode + + def _var_get(self, container, variable, level): + """ + Generate the translation for a variable. + + :param container: A class or namespace. + :param variable: The variable object. + :param level: Recursion level controls indentation. + :return: A string. + """ + sip = { + "name": variable.spelling, + "annotations": set(), + } + modulecode = {} + for child in variable.get_children(): + text = self.source_processor.unpreprocessed(child.extent) + if self.skippable_attribute(variable, child, text, sip): + if not sip["name"]: + return "", modulecode + else: + SipGenerator._report_ignoring(child, "unusable") + # + # Flesh out the SIP context for the rules engine. + # + the_type = variable.type + if the_type.get_canonical().is_a_function: + # + # SIP does not generally like function pointers. Here the problem + # is that variables just don't support canonical function pointers, + # so use the typedef if one is known. Else, rules are needed to fix + # them up. See rule_helpers.container_add_typedefs(). + # + if the_type.spelling.find("(") == -1: + decl = the_type.spelling + else: + the_type = the_type.get_canonical() + decl = the_type.fmt_args() + else: + decl = the_type.get_canonical().spelling + sip["decl"] = decl + # + # Before the rules have run, add/remove any prefix. + # + self._var_get_keywords(container, variable, sip) + modifying_rule = self.compiled_rules.variable_rules().apply(container, variable, sip) + pad = " " * (level * 4) + if sip["name"]: + decl = "" + if modifying_rule: + decl += pad + trace_modified_by(variable, modifying_rule) + decl += self._var_render(variable, sip, pad) + # + # SIP does not support protected variables, so we ignore them. + # + if variable.access_specifier == AccessSpecifier.PROTECTED: + decl = pad + trace_discarded_by(variable, "protected handling") + return decl, {} + if sip["modulecode"]: + modulecode.update(sip["modulecode"]) + else: + decl = pad + trace_discarded_by(variable, modifying_rule) + return decl, modulecode + + def _var_render(self, variable, sip, pad): + """ + Render a variable as output text. + + :param variable: + :param sip: + :param pad: + :return: + """ + the_type = variable.type + decl = sip["decl"] + space = ("" if decl[-1] in "*&" else " ") + if the_type.get_canonical().is_a_function: + # + # SIP does not generally like function pointers, so keep any typedef. + # + if the_type.spelling.find("(") == -1: + decl = decl + space + sip["name"] + else: + the_type = the_type.get_canonical() + result = the_type.fmt_result() + decl = "{}({})({})".format(result, sip["name"], sip["decl"]) + else: + prefixes, type_, operators, dims = utils.decompose_type(sip["decl"]) + decl = "".join(prefixes) + decl += type_ + decl += " " + decl += "".join(operators) + decl += sip["name"] + decl += "".join(dims) + decl = pad + decl + if sip["annotations"]: + decl += " /" + ",".join(sip["annotations"]) + "/" + decl = decl + sip["code"] + ";\n" + return decl + + def _var_get_keywords(self, container, variable, sip): + """ + The parser does not provide direct access to the complete keywords (static, etc) of a variable + in the displayname. It would be nice to get these from the AST, but I cannot find where they are hiding. + + :param container: The variable's container. + :param variable: The variable object. + :param sip: The variable's sip. The decl will be updated with any prefix keywords. + """ + # + # HACK...we seem to get "char const[5]" instead of "const char[5]". + # + if re.search(r" const\b", sip["decl"]): + sip["decl"] = "const " + sip["decl"].replace(" const", "") + if "const " in sip["decl"]: + sip["annotations"].add("NoSetter") + if re.search(r"\w\[", sip["decl"]): + sip["decl"] = sip["decl"].replace("[", " [").replace("] [", "][") + prefix = "" + if variable.storage_class == StorageClass.STATIC: + prefix += "static " + elif variable.storage_class == StorageClass.EXTERN: + prefix += "extern " + sip["decl"] = prefix + sip["decl"] + + @staticmethod + def _report_ignoring(child, reason): + logger.debug(_("Ignoring {} {}").format(reason, item_describe(child))) + + +def main(argv=None): + """ + Take a single C++ header file and generate the corresponding SIP file. + Beyond simple generation of the SIP file from the corresponding C++ + header file, a set of rules can be used to customise the generated + SIP file. + + Examples: + + INC=/usr/include + QT5=$INC/x86_64-linux-gnu/qt5 + KF5=$INC/KF5 + FLAGS="_I$QT5;_I$QT5/QtCore;_I$KF5/kjs;_I$KF5/wtf" + sip_generator.py --flags="$FLAGS" /usr/lib/x86_64-linux-gnu/libclang-3.9.so PyKF5 $KF5/kjs/kjsinterpreter.h tmp.sip + """ + if argv is None: + argv = sys.argv + parser = argparse.ArgumentParser(epilog=inspect.getdoc(main), + formatter_class=HelpFormatter) + parser.add_argument("-v", "--verbose", action="store_true", default=False, help=_("Enable verbose output")) + parser.add_argument("--flags", default="", + help=_("Semicolon-separated C++ compile flags to use, with leading _/__ instead of -/--")) + parser.add_argument("--include_filename", help=_("C++ header include to compile")) + parser.add_argument("--dump-rule-usage", action="store_true", default=False, + help=_("Debug dump rule usage statistics")) + parser.add_argument("libclang", help=_("libclang library to use for parsing")) + parser.add_argument("rules", help=_("Project rules package")) + parser.add_argument("source", help=_("C++ header to process")) + parser.add_argument("output", help=_("output filename to write")) + try: + args = parser.parse_args(argv[1:]) + if args.verbose: + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)s %(levelname)s: %(message)s') + else: + logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') + flags = [] + for f in args.flags.lstrip().split(";"): + if f.startswith("_"): + flags.append("-" + f[1:]) + elif f.startswith("__"): + flags.append("--" + f[2:]) + elif f: + raise argparse.ArgumentTypeError(_("Flags must start with _ or __ in '{}'").format(args.flags)) + # + # Load the given libclang. + # + Config.set_library_file(args.libclang) + exe_clang = "clang++-3.9" + # + # Generate! + # + rules_pkg = os.path.normpath(args.rules) + if rules_pkg.endswith("__init__.py"): + rules_pkg = os.path.dirname(rules_pkg) + elif rules_pkg.endswith(".py"): + rules_pkg = rules_pkg[:-3] + g = SipGenerator(exe_clang, rules_pkg, flags, verbose=args.verbose) + body, modulecode, includes = g.create_sip(args.source, args.include_filename) + with open(args.output, "w") as f: + # + # The modulecode dictionary ensures there can be no duplicates, even if multiple sip files might have + # contributed the same item. By emitting it here, it can provide declare-before-use (needed for + # %Exceptions). + # + for mc in sorted(modulecode): + f.write("\n\n") + f.write(modulecode[mc]) + f.write("\n\n") + f.write(body) + # + # Dump a summary of the rule usage. + # + if args.dump_rule_usage: + # + # Fill the dict of the used rules. + # + def add_usage(rule, usage_count): + rule_usage[str(rule)] = usage_count + + rule_usage = {} + g.compiled_rules.dump_unused(add_usage) + for rule in sorted(rule_usage.keys()): + usage_count = rule_usage[rule] + if usage_count: + logger.info(_("Rule {} used {} times".format(rule, usage_count))) + else: + logger.warn(_("Rule {} was not used".format(rule))) + except Exception as e: + tbk = traceback.format_exc() + print(tbk) + return -1 + + +if __name__ == "__main__": + if sys.argv[-1] != "--self-check": + sys.exit(main()) + else: + Config.set_library_file(sys.argv[2]) Index: find-modules/module_generation/templates/PyQt.py =================================================================== --- /dev/null +++ find-modules/module_generation/templates/PyQt.py @@ -0,0 +1,649 @@ +# +# Copyright 2016 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +SIP binding code for PyQt-template classes. The main content is: + + - FunctionDb, ParameterDb and TypeCodeDb-compatible entries usable in + RuleSets (e.g. {dict,list,set}_{fn_result,parameter,typecode}). + + - {Pair,Pointer}Expander classes which implement corresponding templates. + +This is supported by other public methods and classes which can be used as +examples and/or helper code. +""" + +import gettext +import logging +import os +import re + +import builtin_rules +import templates.mappedtype +import templates.methodcode +from utils import HeldAs +from clangcparser import TypeKind + +gettext.install(os.path.basename(__file__)) +logger = logging.getLogger(__name__) + +# Keep PyCharm happy. +_ = _ + +TYPE = "type" +ARGS = "args" +KNOWN_PTRS = "(?P<" + TYPE + ">Q(Weak|((Explicitly|)Shared(Data|)))Pointer)" +RE_KNOWN_PTRS = re.compile("(const )?" + KNOWN_PTRS + "<(?P<" + ARGS + ">.*)>( [&*])?") + +RE_DICT_T = "(const )?(QHash|QMap)<(.*)>( [&*])?" +RE_LIST_T = "(const )?(QList|QVector)<(.*)>( [&*])?" +RE_SET_T = "(const )?QSet<(.*)>( [&*])?" +RE_PAIR_T = "(const )?QPair<(.*)>( [&*])?" +RE_PTRS_T = "(const )?" + KNOWN_PTRS + "<(.*)>( [&*])?" + +RE_DICT_V = RE_DICT_T + ".*" +RE_LIST_V = RE_LIST_T + ".*" +RE_SET_V = RE_SET_T + ".*" +RE_PAIR_V = RE_PAIR_T + ".*" +RE_PTRS_V = RE_PTRS_T + ".*" + +RE_UNTEMPLATED_FN = ".*[^>]" + + +class DictHelperKey(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_v += ".key()" + return super(DictHelperKey, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(DictHelperKey, self).py_to_cxx(name, needs_reference, py_v) + + +class DictHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_v += ".value()" + return super(DictHelperValue, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(DictHelperValue, self).py_to_cxx(name, needs_reference, py_v) + + +class ListHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_v += "->at(i)" + return super(ListHelperValue, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(ListHelperValue, self).py_to_cxx(name, needs_reference, py_v) + + +class SetHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_v = "*" + cxx_v + return super(SetHelperValue, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(SetHelperValue, self).py_to_cxx(name, needs_reference, py_v) + + +class FunctionParameterHelper(templates.methodcode.FunctionParameterHelper): + """ + Automatic handling for templated function parameter types with auto-unwrapping + of KNOWN_PTRS. + """ + def __init__(self, cxx_t, clang_t, manual_t=None): + is_pointer = RE_KNOWN_PTRS.match(cxx_t) + if is_pointer: + template_type = is_pointer.group(TYPE) + template_args = [is_pointer.group(ARGS)] + cxx_t = template_args[0] + " *" + super(FunctionParameterHelper, self).__init__(cxx_t, clang_t, manual_t) + self.is_pointer = is_pointer + + def cxx_to_py(self, name, needs_reference, cxx_v): + if self.is_pointer: + cxx_v += ".data()" + return super(FunctionParameterHelper, self).cxx_to_py(name, needs_reference, cxx_v) + + def cxx_to_cxx(self, aN, original_type, is_out_paramter): + if self.is_pointer: + code = """ typedef """ + HeldAs.base_type(original_type) + """ Cxx{aN}T; + Cxx{aN}T *cxx{aN} = new Cxx{aN}T({aN}); +""" + code = code.replace("{aN}", aN, 4) + # + # A QWeakPointer has to be constructed via an intermediate QSharedPointer. + # + is_weakptr = re.match("(const )?QWeakPointer<(.*)>( .)?", original_type) + if is_weakptr: + code = code.replace("{aN}", "QSharedPointer<" + is_weakptr.group(2) + ">(" + aN + ")") + else: + code = code.replace("{aN}", aN) + aN = "*cxx" + aN + return code, aN + return super(FunctionParameterHelper, self).cxx_to_cxx(aN, original_type, is_out_paramter) + + def py_parameter(self, type_, name, default, annotations): + if self.is_pointer and default: + # + # TODO: We really just want default.data() as the default value, but SIP gets confused. + # + default = "NULL" + return super(FunctionParameterHelper, self).py_parameter(type_, name, default, annotations) + + +class FunctionReturnHelper(templates.methodcode.FunctionReturnHelper): + """ + Automatic handling for templated function return types with auto-unwrapping + of KNOWN_PTRS templates. + """ + def __init__(self, cxx_t, clang_t, manual_t=None): + is_pointer = RE_KNOWN_PTRS.match(cxx_t) + if is_pointer: + template_type = is_pointer.group(TYPE) + template_args = [is_pointer.group(ARGS)] + cxx_t = template_args[0] + " *" + super(FunctionReturnHelper, self).__init__(cxx_t, clang_t, manual_t) + self.is_pointer = is_pointer + + def cxx_to_py(self, name, needs_reference, cxx_v): + if self.is_pointer: + cxx_v += ".data()" + return super(FunctionReturnHelper, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_fn_result(self, is_constructor): + if self.is_pointer: + return self.cxx_t + return super(FunctionReturnHelper, self).py_fn_result(is_constructor) + + +class PairExpander(templates.mappedtype.AbstractExpander): + + def __init__(self, first_helper, second_helper): + super(PairExpander, self).__init__([("first", first_helper), ("second", second_helper)]) + + def expand_generic(self, qt_type, entries): + """ + Generic support for QPair types which are mapped onto a Python tuple. Either + template parameter can be of any integral (int, long, enum) type or + non-integral type, for example, QPair. + + :param qt_type: The name of the Qt template, e.g. "QPair". + :param entries: Dictionary describing the C++ template. Expected keys: + + first Is the value integral, pointer or object? + second Is the value integral, pointer or object? + """ + first_h = entries["first"] + second_h = entries["second"] + code = """ +%TypeHeaderCode +#include <{header_h}> +%End +%ConvertFromTypeCode +""" + code += first_h.declare_type_helpers("first", "return 0;") + code += second_h.declare_type_helpers("second", "return 0;") + code += """ + // Create the tuple + PyObject *tuple = NULL; + { +""" + code += first_h.cxx_to_py("first", True, "sipCpp") + code += second_h.cxx_to_py("second", True, "sipCpp") + code += """ tuple = (first && second) ? PyTuple_Pack(2, first, second) : NULL; +""" + # + # Error handling assumptions: + # + # - a failed "new" throws (or, not compliant, return NULL). + # - the good path should be as fast as possible, error recovery can be slow. + # + code += """ + if (first == NULL || second == NULL || tuple == NULL) { + PyErr_Format(PyExc_TypeError, "cannot combine first/second as tuple"); +""" + code += first_h.decrement_python_reference("first") + second_h.decrement_python_reference("second") + if first_h.category == HeldAs.OBJECT: + code += """ delete first; +""" + if second_h.category == HeldAs.OBJECT: + code += """ delete second; +""" + code += """ Py_XDECREF(tuple); + return 0; + } + Py_DECREF(first); + Py_DECREF(second); + } + return tuple; +%End +%ConvertToTypeCode +""" + code += first_h.declare_type_helpers("first", "return 0;", need_string=first_h.category != HeldAs.INTEGER) + code += second_h.declare_type_helpers("second", "return 0;", need_string=second_h.category != HeldAs.INTEGER) + code += """ PyObject *first; + PyObject *second; + Py_ssize_t i = 0; + + // Silently check the sequence if that is all that is required. + if (sipIsErr == NULL) { + return (PySequence_Check(sipPy) +#if PY_MAJOR_VERSION < 3 + && !PyString_Check(sipPy) +#endif + && !PyUnicode_Check(sipPy)); + i = PySequence_Size(sipPy); + if (i != 2) { + // A negative length should only be an internal error so let the original exception stand. + if (i >= 0) { + PyErr_Format(PyExc_TypeError, "sequence has %zd elements but 2 elements are expected", i); + } +""" + code += first_h.check_python_type("first") + code += second_h.check_python_type("second") + code += """ } + return 1; + } else if (*sipIsErr) { + return 0; + } + + // Convert the sequence to C++. + {qt_type} *pair = new {qt_type}(); + { +""" + code += first_h.py_to_cxx("first", True, "PySequence_ITEM(sipPy, 0)") + code += second_h.py_to_cxx("second", True, "PySequence_ITEM(sipPy, 1)") + code += """ + if (*sipIsErr) { +""" + if first_h.category != HeldAs.INTEGER: + code += """ if (cxxfirst == NULL) { + PyErr_Format(PyExc_TypeError, "tuple first has type '%s' but '%s' is expected", + Py_TYPE(first)->tp_name, cxxfirstS); + } +""" + if second_h.category != HeldAs.INTEGER: + code += """ if (cxxsecond == NULL) { + PyErr_Format(PyExc_TypeError, "tuple second has type '%s' but '%s' is expected", + Py_TYPE(second)->tp_name, cxxsecondS); + } +""" + code += first_h.release_sip_helper("first") + code += second_h.release_sip_helper("second") + code += """ delete pair; + return 0; + } + pair->first = """ + code += first_h.insertable_cxx_value("first") + """; + pair->second = """ + code += second_h.insertable_cxx_value("second") + code += """; +""" + code += first_h.release_sip_helper("first") + code += second_h.release_sip_helper("second") + code += """ } + *sipCppPtr = pair; + return sipGetState(sipTransferObj); +%End +""" + code = code.replace("{header_h}", self.header_for(qt_type), 1) + code = code.replace("{qt_type}", qt_type) + code = code.replace("{first_t}", first_h.cxx_t) + code = code.replace("{second_t}", second_h.cxx_t) + return code + + +class PairHelperFirst(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_v += "->first" + return super(PairHelperFirst, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(PairHelperFirst, self).py_to_cxx(name, needs_reference, py_v) + + +class PairHelperSecond(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_v += "->second" + return super(PairHelperSecond, self).cxx_to_py(name, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(PairHelperSecond, self).py_to_cxx(name, needs_reference, py_v) + + +class PointerExpander(templates.mappedtype.AbstractExpander): + + def __init__(self, value_helper): + super(PointerExpander, self).__init__([("value", value_helper)]) + + def expand_generic(self, qt_type, entries): + """ + Generic support for Qt pointer types. The template parameter can be of + any integral (int, long, enum) type or non-integral type, for example, + QWeakPointer. + + :param qt_type: The name of the Qt template, e.g. "QSharedDataPointer". + :param entries: Dictionary describing the C++ template. Expected keys: + + value Is the value integral, pointer or object? + """ + value_h = entries["value"] + code = """ +%TypeHeaderCode +#include <{header_h}> +%End +%ConvertFromTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;") + code += """ + // Convert the value from C++. +""" + code += value_h.cxx_to_py("value", True, "sipCpp") + code += """ if (value == NULL) { + PyErr_Format(PyExc_TypeError, "cannot convert value"); + return 0; + } + return value; +%End +%ConvertToTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;", need_string=value_h.category != HeldAs.INTEGER) + code += """ PyObject *value; + + // Convert the value to C++. + value = sipPy; +""" + code += value_h.py_to_cxx("value", True, "value") + code += """ + if (*sipIsErr) { +""" + if value_h.category != HeldAs.INTEGER: + code += """ if (cxxvalue == NULL) { + PyErr_Format(PyExc_TypeError, "value has type '%s' but '%s' is expected", + Py_TYPE(value)->tp_name, cxxvalueS); + } +""" + code += value_h.release_sip_helper("value") + code += """ return 0; + } +""" + code += value_h.release_sip_helper("value") + if qt_type == "QWeakPointer": + # + # A QWeakPointer has to be constructed via an intermediate QSharedPointer. + # + code += """ *sipCppPtr = new {qt_type}(QSharedPointer(cxxvalue)); +""" + else: + code += """ *sipCppPtr = new {qt_type}(cxxvalue); +""" + if value_h.cxx_t.startswith("QExplicitlySharedDataPointer"): + code += """ cxxvalue->ref.deref(); +""" + code += """ return sipGetState(sipTransferObj); +%End +""" + code = code.replace("{header_h}", self.header_for(qt_type), 1) + code = code.replace("{qt_type}", qt_type) + code = code.replace("{sip_t}", value_h.sip_t) + return code + + +class PointerHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py_value(self, name, cxx_v, transfer): + """An expression converting the named C++ value to Python.""" + cxx_v += "->data()" + code = "sipConvertFromType((void *){cxx_v}, gen{name}T, sipTransferObj)" + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + return code + + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + code = """ PyObject *{name} = {cxx_to_py_value}; +""" + if self.cxx_t.startswith("QExplicitlySharedDataPointer"): + code += """ {cxx_v}->ref.ref(); +""" + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{cxx_to_py_value}", cxx_to_py_value) + return code + + +def function_uses_templates(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create %MethodCode expansions + for C++ functions with templated return types and/or parameters. + """ + sip.setdefault("parameter_helper", FunctionParameterHelper) + sip.setdefault("return_helper", FunctionReturnHelper) + builtin_rules.function_uses_templates(container, fn, sip, rule) + + +def dict_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for C++ + types with two template arguments into Python dicts. + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = templates.mappedtype.DictExpander(DictHelperKey, DictHelperValue) + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def dict_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for C++ + types with two template arguments into Python dicts. + """ + template = templates.mappedtype.DictExpander(DictHelperKey, DictHelperValue) + template.expand(rule, parameter, sip["decl"], sip) + + +def dict_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for C++ + types with two template arguments into Python dicts. + """ + template = templates.mappedtype.DictExpander(DictHelperKey, DictHelperValue) + template.expand(rule, typedef, sip["decl"], sip) + + +def list_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python lists. + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = templates.mappedtype.ListExpander(ListHelperValue) + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def list_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python lists. + """ + template = templates.mappedtype.ListExpander(ListHelperValue) + template.expand(rule, parameter, sip["decl"], sip) + + +def list_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python lists. + """ + template = templates.mappedtype.ListExpander(ListHelperValue) + template.expand(rule, typedef, sip["decl"], sip) + + +def set_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python sets. + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = templates.mappedtype.SetExpander(SetHelperValue) + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def set_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python sets. + """ + template = templates.mappedtype.SetExpander(SetHelperValue) + template.expand(rule, parameter, sip["decl"], sip) + + +def set_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python sets. + """ + template = templates.mappedtype.SetExpander(SetHelperValue) + template.expand(rule, typedef, sip["decl"], sip) + + +def pair_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for a + QPair<> (using a 2-tuple). + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = PairExpander(PairHelperFirst, PairHelperSecond) + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def pair_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for a + QPair<> (using a 2-tuple). + """ + template = PairExpander(PairHelperFirst, PairHelperSecond) + template.expand(rule, parameter, sip["decl"], sip) + + +def pair_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for a + QPair<> (using a 2-tuple). + """ + template = PairExpander(PairHelperFirst, PairHelperSecond) + template.expand(rule, typedef, sip["decl"], sip) + + +def pointer_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for a + Qt pointer type. + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = PointerExpander(PointerHelperValue) + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def pointer_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for a + Qt pointer type. + """ + template = PointerExpander(PointerHelperValue) + template.expand(rule, parameter, sip["decl"], sip) + + +def pointer_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for a + Qt pointer type. + """ + template = PointerExpander(PointerHelperValue) + if typedef.underlying_type.kind == TypeKind.ELABORATED: + # + # This is a typedef of a typedef, and Clang gets the template + # parameters wrong, so abort. + # + return + assert typedef.underlying_type.kind == TypeKind.UNEXPOSED + template.expand(rule, typedef, sip["decl"], sip) + + +def function_rules(): + return [ + # + # Supplement Qt templates with %MappedTypes for the function result, + # and call function_uses_templates for %MethodCode too. + # + [".*", RE_UNTEMPLATED_FN, "", RE_DICT_V, ".*", dict_fn_result], + [".*", RE_UNTEMPLATED_FN, "", RE_LIST_V, ".*", list_fn_result], + [".*", RE_UNTEMPLATED_FN, "", RE_SET_V, ".*", set_fn_result], + [".*", RE_UNTEMPLATED_FN, "", RE_PAIR_V, ".*", pair_fn_result], + [".*", RE_UNTEMPLATED_FN, "", RE_PTRS_V, ".*", pointer_fn_result], + # + # Call function_uses_templates...the parameters have been dealt with elsewhere. + # + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_DICT_V, function_uses_templates], + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_LIST_V, function_uses_templates], + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_SET_V, function_uses_templates], + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_PAIR_V, function_uses_templates], + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_PTRS_V, function_uses_templates], + ] + + +def parameter_rules(): + return [ + # + # Supplement Qt templates with %MappedTypes. + # + [".*", RE_UNTEMPLATED_FN, ".*", RE_DICT_V, ".*", dict_parameter], + [".*", RE_UNTEMPLATED_FN, ".*", RE_LIST_V, ".*", list_parameter], + [".*", RE_UNTEMPLATED_FN, ".*", RE_SET_V, ".*", set_parameter], + [".*", RE_UNTEMPLATED_FN, ".*", RE_PAIR_V, ".*", pair_parameter], + [".*", RE_UNTEMPLATED_FN, ".*", RE_PTRS_V, ".*", pointer_parameter], + ] + + +def typedef_rules(): + return [ + # + # Supplement Qt templates with %MappedTypes. + # + [".*", ".*", ".*", RE_DICT_T, dict_typecode], + [".*", ".*", ".*", RE_LIST_T, list_typecode], + [".*", ".*", ".*", RE_SET_T, set_typecode], + [".*", ".*", ".*", RE_PAIR_T, pair_typecode], + [".*", ".*", ".*", RE_PTRS_T, pointer_typecode], + ] Index: find-modules/module_generation/templates/mappedtype.py =================================================================== --- /dev/null +++ find-modules/module_generation/templates/mappedtype.py @@ -0,0 +1,765 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +Generic SIP %MappedType code for C++-template classes. The main content is: + + - The AbstractExpander class which is a model of how template expansion + can be performed as needed. + + - {Dict,List,Set}Expander derived classes which implement corresponding + templates. + + - FunctionDb, ParameterDb and TypeCodeDb-compatible entries usable in + RuleSets (e.g. {dict,list,set}_{fn_result,parameter,typecode}). + +This is supported by other public methods and classes which can be used as +examples and/or helper code. +""" +from abc import ABCMeta, abstractmethod +import gettext +import logging +import os + +import rule_helpers +import utils +from clangcparser import CursorKind, TypeKind +from utils import trace_generated_for, HeldAs + +gettext.install(os.path.basename(__file__)) +logger = logging.getLogger(__name__) + +# Keep PyCharm happy. +_ = _ + + +class AbstractExpander(object): + """ + Defines a basic structure useful for describing the expansion of C++ + templated types as SIP %MappedTypes. + + Rule writers can tailor the expansion using custom subclasses which + override the template itself (@see expand_generic()) and the parameters + (@see __init__()). + """ + __metaclass__ = ABCMeta + + def __init__(self, variables): + """ + Constructor. + + :param variables: The [(string, class)] which characterise the template + to be expanded. + """ + self.variables = [v[0] for v in variables] + self.helpers = {v[0]: v[1] for v in variables} + + @abstractmethod + def expand_generic(self, template_t, entries): + """ + Expand a text-template using the passed parameters. + + :param template_t: The name of the C++ template, such as "QMap" or "std::vector". + :param entries: Dictionary describing the C++ template. Expected keys: + + variable1..N Is the variable integral, pointer or object? + + The variable1..N are (subclasses of) HeldAs. The base + class HeldAs is sufficient for the needs of the default + helper functions (@see categorise, @see actual_type and + @see base_type). + """ + raise NotImplemented(_("Missing subclass")) + + def actual_type(self, manual_override, parameter_text, parameter_cursor): + """ + We would like to be able to use clang type system to determine the parameter type, but structure of the + child cursors of the typedef do not form an AST. In addition, children of int types are not even included. + + TODO: When we figure this out, get rid of the horrid heuristics. + + :param manual_override: The user knows best. + :param parameter_text: The text from the source code. + :param parameter_cursor: The clang object. + :return: the type of the parameter. + """ + if manual_override: + return manual_override + if parameter_cursor: + tmp = parameter_cursor.type.get_canonical().spelling + if parameter_text.endswith(("*", "&")): + tmp += " " + parameter_text[-1] + return tmp + return HeldAs.actual_type(parameter_text) + + def base_type(self, manual_override, parameter_text, parameter_cursor): + """ + We would like to be able to use clang type system to determine the underlying type, but structure of the + child cursors of the typedef do not form an AST. In addition, children of int types are not even included. + + TODO: When we figure this out, get rid of the horrid heuristics. + + :param manual_override: The user knows best. + :param parameter_text: The text from the source code. + :param parameter_cursor: The clang object. + :return: the base_type of the parameter, e.g. without a pointer suffix. + """ + if manual_override: + return manual_override + if parameter_cursor: + return parameter_cursor.type.get_canonical().spelling + return HeldAs.base_type(parameter_text) + + def header_for(self, template_t): + return template_t + + def parse_template(self, template, expected): + """ + Extract template name and arguments even in cases like + 'const QSet > &foo'. + + :return: (name, [args]) + """ + name, args = utils.decompose_template(template) + assert len(args) == expected, "Expected {} template arguments in '{}', got {}".format(expected, template, args) + return name, args + + def expand(self, rule, cursor, text, sip): + """ + Expand a template, and return the results via the sip. + + :param rule: The rule requesting the expansion. + :param cursor: The item Cursor for whom the expansion is being performed. + This is typically a typed object, such as "QMap" or "QHash". + :param text: The item text to be expanded. + :param sip: The sip. + + - mt_parameter_helper: Tailored parameter handling. Rule + writers can tailor the expansion of parameters using + custom subclasses of GenerateMappedHelper. + """ + expected_parameters = self.variables + # + # Extract the text forms even in cases like 'QSet >'. + # + text_type, text_args = self.parse_template(text, len(expected_parameters)) + # + # We would like to be able to use clang type system to determine the HELD_AS etc, but the number of children of + # the typedef is variable (i.e. the structure of an AST is not represented). Also, for example, int types are + # not even included. Also, there appears to be no way to get from the cursor's child [CursorKind.TEMPLATE_REF, + # CursorKind.TYPE_REF] to the types. + # + # Compose the parent type, and the dicts for the parameters and a default declaration. + # + manual_types = sip.get("types", [None] * len(expected_parameters)) + manual_base_types = sip.get("base_types", [None] * len(expected_parameters)) + entries = {} + parameters = [] + for i, parameter in enumerate(expected_parameters): + actual_type = manual_types[i] or text_args[i] + base_type = manual_base_types[i] + p = { + "type": actual_type, + "base_type": base_type, + } + entries[parameter] = self.helpers[parameter](p, None) + parameters.append(actual_type) + text_args = ", ".join(text_args) + # + # Run the template handler... + # + if text_args.endswith(">"): + text_args += " " + mapped_type = "{}<{}>".format(text_type, text_args) + trace = trace_generated_for(cursor, rule, ["{}({})".format(entries[p].cxx_t, entries[p].category) + for p in expected_parameters]) + code = self.expand_generic(text_type, entries) + code = "%MappedType " + mapped_type + "\n{\n" + trace + code + "};\n" + sip["modulecode"][mapped_type] = code + + +class DictExpander(AbstractExpander): + + def __init__(self, key_helper, value_helper): + super(DictExpander, self).__init__([("key", key_helper), ("value", value_helper)]) + + def expand_generic(self, template_t, entries): + """ + Generic support for C++ types which map onto a Python dict, such as QMap and + QHash. Either template parameter can be of any integral (int, long, enum) type + or non-integral type, for example, QMap. + + :param template_t: The name of the C++ template, e.g. "QMap". + :param entries: Dictionary describing the C++ template. Expected keys: + + key Is the key integral, pointer or object? + value Is the value integral, pointer or object? + """ + key_h = entries["key"] + value_h = entries["value"] + code = """ +%TypeHeaderCode +#include <{header_h}> +%End +%ConvertFromTypeCode +""" + code += key_h.declare_type_helpers("key", "return 0;") + code += value_h.declare_type_helpers("value", "return 0;") + code += """ + // Create the Python dictionary. + PyObject *dict = PyDict_New(); + if (!dict) { + PyErr_Format(PyExc_TypeError, "unable to create a dict"); + return 0; + } + + // Set the dictionary elements. + {template_t}::const_iterator i = sipCpp->constBegin(); + {template_t}::const_iterator end = sipCpp->constEnd(); + while (i != end) { +""" + code += key_h.cxx_to_py("key", True, "i") + code += value_h.cxx_to_py("value", True, "i") + # + # Error handling assumptions: + # + # - a failed "new" throws (or, not compliant, return NULL). + # - the good path should be as fast as possible, error recovery can be slow. + # + code += """ + if (key == NULL || value == NULL || PyDict_SetItem(dict, key, value) < 0) { + PyErr_Format(PyExc_TypeError, "cannot insert key/value into dict"); +""" + code += key_h.decrement_python_reference("key") + value_h.decrement_python_reference("value") + if key_h.category == HeldAs.OBJECT: + code += """ delete key; +""" + if value_h.category == HeldAs.OBJECT: + code += """ delete value; +""" + code += """ Py_DECREF(dict); + return 0; + } + Py_DECREF(key); + Py_DECREF(value); + ++i; + } + return dict; +%End +%ConvertToTypeCode +""" + code += key_h.declare_type_helpers("key", "return 0;", need_string=key_h.category != HeldAs.INTEGER) + code += value_h.declare_type_helpers("value", "return 0;", need_string=value_h.category != HeldAs.INTEGER) + code += """ PyObject *key; + PyObject *value; + Py_ssize_t i = 0; + + // Silently check the dict if that is all that is required. + if (sipIsErr == NULL) { + if (!PyDict_Check(sipPy)) { + return 0; + } + + while (PyDict_Next(sipPy, &i, &key, &value)) { +""" + code += key_h.check_python_type("key") + code += value_h.check_python_type("value") + code += """ } + return 1; + } else if (*sipIsErr) { + return 0; + } + if (!PyDict_Check(sipPy)) { + PyErr_Format(PyExc_TypeError, "expected dict"); + *sipIsErr = 1; + return 0; + } + + // Convert the dict to C++. + {template_t} *dict = new {template_t}(); + while (PyDict_Next(sipPy, &i, &key, &value)) { +""" + code += key_h.py_to_cxx("key", True, "key") + code += value_h.py_to_cxx("value", True, "value") + code += """ + if (*sipIsErr) { +""" + if key_h.category != HeldAs.INTEGER: + code += """ if (cxxkey == NULL) { + PyErr_Format(PyExc_TypeError, "a dict key has type '%s' but '%s' is expected", + Py_TYPE(key)->tp_name, cxxkeyS); + } +""" + if value_h.category != HeldAs.INTEGER: + code += """ if (cxxvalue == NULL) { + PyErr_Format(PyExc_TypeError, "a dict value has type '%s' but '%s' is expected", + Py_TYPE(value)->tp_name, cxxvalueS); + } +""" + code += key_h.release_sip_helper("key") + code += value_h.release_sip_helper("value") + code += """ delete dict; + return 0; + } + dict->insert(""" + code += key_h.insertable_cxx_value("key") + ", " + value_h.insertable_cxx_value("value") + code += """); +""" + code += key_h.release_sip_helper("key") + code += value_h.release_sip_helper("value") + code += """ } + *sipCppPtr = dict; + return sipGetState(sipTransferObj); +%End +""" + code = code.replace("{header_h}", self.header_for(template_t), 1) + code = code.replace("{template_t}", template_t) + code = code.replace("{key_t}", key_h.cxx_t) + code = code.replace("{value_t}", value_h.cxx_t) + return code + + +class ListExpander(AbstractExpander): + + def __init__(self, value_helper): + super(ListExpander, self).__init__([("value", value_helper)]) + + def expand_generic(self, template_t, entries): + """ + Generic support for C++ types which map onto a Python list, such as QList and + QVector. The template parameter can be of any integral (int, long, enum) type + or non-integral type, for example, QList or std::vector. + + :param template_t: The name of the C++ template, e.g. "QList". + :param entries: Dictionary describing the C++ template. Expected keys: + + value Is the value integral, pointer or object? + """ + value_h = entries["value"] + code = """ +%TypeHeaderCode +#include <{header_h}> +%End +%ConvertFromTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;") + code += """ + // Create the Python list. + PyObject *list = PyList_New(sipCpp->size()); + if (!list) { + PyErr_Format(PyExc_TypeError, "unable to create a list"); + return 0; + } + + // Set the list elements. + Py_ssize_t i = 0; + for (i = 0; i < (Py_ssize_t)sipCpp->size(); ++i) { +""" + code += value_h.cxx_to_py("value", True, "sipCpp") + code += """ + if (value == NULL) { + PyErr_Format(PyExc_TypeError, "cannot insert value into list"); +""" + code += value_h.decrement_python_reference("value") + code += """ Py_DECREF(list); + return 0; + } else { + PyList_SET_ITEM(list, i, value); + } + } + return list; +%End +%ConvertToTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;", need_string=value_h.category != HeldAs.INTEGER) + code += """ PyObject *value; + Py_ssize_t i = 0; + + // Silently check the list if that is all that is required. + if (sipIsErr == NULL) { + if (!PyList_Check(sipPy)) { + return 0; + } + + for (i = 0; i < PyList_GET_SIZE(sipPy); ++i) { + value = PyList_GET_ITEM(sipPy, i); +""" + code += value_h.check_python_type("value",) + code += """ } + return 1; + } else if (*sipIsErr) { + return 0; + } + if (!PyList_Check(sipPy)) { + PyErr_Format(PyExc_TypeError, "expected list"); + *sipIsErr = 1; + return 0; + } + + // Convert the list to C++. + {template_t} *list = new {template_t}(); + list->reserve(PyList_GET_SIZE(sipPy)); + for (i = 0; i < PyList_GET_SIZE(sipPy); ++i) { + value = PyList_GET_ITEM(sipPy, i); +""" + code += value_h.py_to_cxx("value", True, "value") + code += """ + if (*sipIsErr) { +""" + if value_h.category != HeldAs.INTEGER: + code += """ if (cxxvalue == NULL) { + PyErr_Format(PyExc_TypeError, "list value %ld has type '%s' but '%s' is expected", i, + Py_TYPE(value)->tp_name, cxxvalueS); + } +""" + code += value_h.release_sip_helper("value") + code += """ delete list; + return 0; + } + list->push_back(""" + code += value_h.insertable_cxx_value("value") + code += """); +""" + code += value_h.release_sip_helper("value") + code += """ } + *sipCppPtr = list; + return sipGetState(sipTransferObj); +%End +""" + code = code.replace("{header_h}", self.header_for(template_t), 1) + code = code.replace("{template_t}", template_t) + code = code.replace("{value_t}", value_h.cxx_t) + return code + + +class SetExpander(AbstractExpander): + + def __init__(self, value_helper): + super(SetExpander, self).__init__([("value", value_helper)]) + + def expand_generic(self, template_t, entries): + """ + Generic support for C++ types which map onto a Python set, such as + QSet. The template parameter can be of any integral (int, long, + enum) type or non-integral type, for example, QSet or + QSet. + + :param template_t: The name of the C++ template, e.g. "QSet". + :param entries: Dictionary describing the C++ template. Expected keys: + + value Is the item integral, pointer or object? + """ + value_h = entries["value"] + code = """ +%TypeHeaderCode +#include <{header_h}> +%End +%ConvertFromTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;") + code += """ + // Create the Python set. + PyObject *set = PySet_New(NULL); + if (!set) { + PyErr_Format(PyExc_TypeError, "unable to create a set"); + return 0; + } + + // Set the set elements. + {template_t}::const_iterator i = sipCpp->constBegin(); + {template_t}::const_iterator end = sipCpp->constEnd(); + while (i != end) { +""" + code += value_h.cxx_to_py("value", True, "i") + code += """ + if (value == NULL || PySet_Add(set, value) < 0) { + PyErr_Format(PyExc_TypeError, "cannot insert value into set"); +""" + code += value_h.decrement_python_reference("value") + code += """ Py_DECREF(set); + return 0; + } + ++i; + } + return set; +%End +%ConvertToTypeCode +""" + code += value_h.declare_type_helpers("value", "*sipIsErr = 1;", need_string=value_h.category != HeldAs.INTEGER) + code += """ PyObject *value; + PyObject *i = PyObject_GetIter(sipPy); + if (i == NULL) { + if (sipIsErr) { + PyErr_Format(PyExc_TypeError, "unable to allocate set iterator"); + *sipIsErr = 1; + } + return 0; + } + + // Silently check the set if that is all that is required. + if (sipIsErr == NULL) { + if (!PySet_Check(sipPy)) { + Py_DECREF(i); + return 0; + } + + while ((value = PyIter_Next(i)) != NULL) { +""" + code += value_h.check_python_type("value", "Py_DECREF(i);\n ") + code += """ } + Py_DECREF(i); + return 1; + } else if (*sipIsErr) { + Py_DECREF(i); + return 0; + } + if (!PySet_Check(sipPy)) { + Py_DECREF(i); + PyErr_Format(PyExc_TypeError, "expected set"); + *sipIsErr = 1; + return 0; + } + + // Convert the set to C++. + {template_t} *set = new {template_t}(); + set->reserve(PySet_GET_SIZE(sipPy)); + while ((value = PyIter_Next(i)) != NULL) { +""" + code += value_h.py_to_cxx("value", True, "value") + code += """ + if (*sipIsErr) { +""" + if value_h.category != HeldAs.INTEGER: + code += """ if (cxxvalue == NULL) { + PyErr_Format(PyExc_TypeError, "a set value has type '%s' but '%s' is expected", + Py_TYPE(value)->tp_name, cxxvalueS); + } +""" + code += value_h.release_sip_helper("value") + code += """ delete set; + Py_DECREF(i); + return 0; + } + set->insert(""" + code += value_h.insertable_cxx_value("value") + code += """); +""" + code += value_h.release_sip_helper("value") + code += """ } + Py_DECREF(i); + *sipCppPtr = set; + return sipGetState(sipTransferObj); +%End +""" + code = code.replace("{header_h}", self.header_for(template_t), 1) + code = code.replace("{template_t}", template_t) + code = code.replace("{value_t}", value_h.cxx_t) + return code + + +class GenerateMappedHelper(HeldAs): + def __init__(self, entry, clang_t): + super(GenerateMappedHelper, self).__init__(entry["type"], clang_t, entry["base_type"]) + + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + options = { + HeldAs.INTEGER: + """ PyObject *{name} = {cxx_to_py_value}; +""", + HeldAs.FLOAT: + """ PyObject *{name} = {cxx_to_py_value}; +""", + HeldAs.POINTER: + """ Cxx{name}T cxx{name} = {cxx_v}; + PyObject *{name} = {cxx_to_py_value}; +""", + HeldAs.OBJECT: + """ Cxx{name}T *cxx{name} = new Cxx{name}T({cxx_v}); + PyObject *{name} = {cxx_to_py_value}; +""", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{cxx_to_py_value}", cxx_to_py_value) + return code + + def py_to_cxx_template(self, name, py_v, py_to_cxx_value): + options = { + HeldAs.INTEGER: + """ Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.FLOAT: + """ Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.POINTER: + """ int {name}State; + Cxx{name}T cxx{name} = {py_to_cxx_value}; +""", + HeldAs.OBJECT: + """ int {name}State; + Cxx{name}T *cxx{name} = {py_to_cxx_value}; +""", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{py_v}", py_v) + code = code.replace("{py_to_cxx_value}", py_to_cxx_value) + return code + + def py_to_cxx_value(self, name, py_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong({name}) +#else + (Cxx{name}T)PyInt_AsLong({name}) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble({name})", + HeldAs.POINTER: + "reinterpret_cast(sipForceConvertToType({name}, gen{name}T, sipTransferObj, SIP_NOT_NONE, &{name}State, sipIsErr))", + HeldAs.OBJECT: + "reinterpret_cast(sipForceConvertToType({name}, gen{name}T, sipTransferObj, SIP_NOT_NONE, &{name}State, sipIsErr))", + } + ptr_options = { + HeldAs.BYTE: + "(Cxx{name}T)PyString_AsString({name})", + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong(*({name})) +#else + (Cxx{name}T)PyInt_AsLong(*({name})) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble(*({name}))", + } + + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{py_v}", py_v) + return code + + def decrement_python_reference(self, name): + code = """ Py_XDECREF({name}); +""" + code = code.replace("{name}", name) + return code + + def check_python_type(self, name, extra=""): + options = { + HeldAs.INTEGER: + """#if PY_MAJOR_VERSION >= 3 + if (!PyLong_Check({name})) { + {extra}return 0; + } +#else + if (!PyInt_Check({name})) { + {extra}return 0; + } +#endif +""", + HeldAs.POINTER: + """ if (!sipCanConvertToType({name}, gen{name}T, SIP_NOT_NONE)) { + {extra}return 0; + } +""", + HeldAs.FLOAT: + """ if (!PyFloat_Check({name})) { + {extra}return 0; + } +""", + } + ptr_options = { + HeldAs.BYTE: + """if (!PyString_Check({name})) { + {extra}return 0; + } +""", + HeldAs.INTEGER: + """#if PY_MAJOR_VERSION >= 3 + if (!PyLong_Check({name})) { + {extra}return 0; + } +#else + if (!PyInt_Check({name})) { + {extra}return 0; + } +#endif +""", + HeldAs.FLOAT: + """if (!PyFloat_Check({name})) { + {extra}return 0; + } +""", + } + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + options[HeldAs.OBJECT] = options[HeldAs.POINTER] + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{extra}", extra) + return code + + def release_sip_helper(self, name): + options = { + HeldAs.INTEGER: + "", + HeldAs.POINTER: + """ sipReleaseType((void *)cxx{name}, gen{name}T, {name}State); +""", + HeldAs.FLOAT: + "", + } + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = "" + else: + options[HeldAs.OBJECT] = options[HeldAs.POINTER] + code = options[self.category] + code = code.replace("{name}", name) + return code + + def insertable_cxx_value(self, name): + options = { + HeldAs.INTEGER: "cxx{name}", + HeldAs.POINTER: "cxx{name}", + HeldAs.OBJECT: "*cxx{name}", + HeldAs.FLOAT: "cxx{name}", + } + code = options[self.category] + code = code.replace("{name}", name) + return code Index: find-modules/module_generation/templates/methodcode.py =================================================================== --- /dev/null +++ find-modules/module_generation/templates/methodcode.py @@ -0,0 +1,439 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +Generic SIP %MethodCode code for C++ functions which use templated parameters +and/or return types. The main content is: + + - The FunctionExpander class which is a model of how template expansion + can be performed as needed. + + - Function{Parameter,Return}Helper base classes which implement customisation. + + - FunctionDb-compatible action (function_uses_templates) usable in + RuleSets. + +This is supported by other public methods and classes which can be used as +examples and/or helper code. +""" +import gettext +import logging +import os +import re + +from clang.cindex import AccessSpecifier + +import rule_helpers +import utils +from clangcparser import CursorKind +from rule_helpers import initialise_cxx_decl +from utils import fqn, trace_generated_for, HeldAs + +gettext.install(os.path.basename(__file__)) +logger = logging.getLogger(__name__) + +# Keep PyCharm happy. +_ = _ + + +RE_PARAMETER_VALUE = re.compile(r"\s*=\s*") +RE_PARAMETER_TYPE = re.compile(r"(.*[ >&*])(.*)") + + +class FunctionParameterHelper(HeldAs): + """ + Function parameter helper base class for FunctionExpander. + Use subclasses to customise the output from FunctionExpander. + """ + def cxx_to_py(self, py_v, needs_reference, cxx_v): + return " Cxx{}T cxx{} = {};\n".format(py_v, py_v, cxx_v) + + def cxx_to_cxx(self, aN, original_type, is_out_paramter): + if not is_out_paramter: + # + # Assume a pointer to a non-HeldAs.BYTE POD is an "out". + # + is_out_paramter = (self.category == HeldAs.POINTER and + self.sip_t in [HeldAs.INTEGER, HeldAs.FLOAT]) + code = "" + if self.category == HeldAs.OBJECT: + aN = "*" + aN + elif is_out_paramter and not self.cxx_t.endswith("&"): + aN = "&" + aN + return code, aN + + def py_parameter(self, type, name, default, annotations): + if not self.cxx_t.endswith(("*", "&")): + name = " " + name + if default: + return "{}{}{} = {}".format(self.cxx_t, name, annotations, default) + else: + return "{}{}{}".format(self.cxx_t, name, annotations) + + +class FunctionReturnHelper(HeldAs): + """ + Function return value helper base class for FunctionExpander. + Use subclasses to customise the output from FunctionExpander. + """ + def declare_type_helpers(self, name, error): + return super(FunctionReturnHelper, self).declare_type_helpers(name, error, need_cxx_t=False) + + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + if self.category in [HeldAs.POINTER, HeldAs.OBJECT]: + code = """ {name} = {cxx_to_py_value}; + return sipConvertFromType((void *){name}, gen{name}T, NULL); +""" + else: + code = """ {name} = {cxx_v}; + return {cxx_to_py_value}; +""" + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{cxx_to_py_value}", cxx_to_py_value) + return code + + def cxx_to_py_value(self, name, cxx_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + PyLong_FromLong((long){name}) +#else + PyInt_FromLong((long){name}) +#endif +)""", + HeldAs.FLOAT: + "PyFloat_FromDouble((double){name})", + HeldAs.POINTER: + "const_cast({cxx_v})", + HeldAs.OBJECT: + "const_cast(&{cxx_v})", + } + + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + return code + + def py_to_cxx_template(self, name, py_v, py_to_cxx_value): + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = " Cxx{name}T cxx{name} = {py_to_cxx_value};" + elif self.category in [HeldAs.POINTER, HeldAs.OBJECT]: + code = """ int {name}State; + Cxx{name}T cxx{name} = {py_to_cxx_value}; +""" + else: + code = " Cxx{name}T cxx{name} = {py_to_cxx_value};" + code = code.replace("{name}", name) + code = code.replace("{py_v}", py_v) + code = code.replace("{py_to_cxx_value}", py_to_cxx_value) + return code + + def py_fn_result(self, is_constructor): + if not is_constructor and self.category == HeldAs.OBJECT and not self.cxx_t.endswith("&"): + return self.cxx_t + " *" + return self.cxx_t + + +class FunctionExpander(object): + """ + Automatic handling for functions with templated parameters and/or return types. + """ + def __init__(self, parameter_helper, return_helper): + """ + + :param parameter_helper: (Subclass of) FunctionParameterHelper. + :param return_helper: (Subclass of) FunctionReturnHelper. + """ + super(FunctionExpander, self).__init__() + self.parameter_helper = parameter_helper + self.return_helper = return_helper + + def expand_template(self, function, entries): + """ + :param function: The CursorKind for the function. + :param entries: Dictionary describing the C++ template. Expected keys: + + result Is the return type integral, pointer or object? + parameters Is the parameter integral, pointer or object? + """ + def in_class(item): + parent = item.semantic_parent + while parent and parent.kind not in [CursorKind.CLASS_DECL, CursorKind.TRANSLATION_UNIT]: + parent = parent.semantic_parent + return parent.kind == CursorKind.CLASS_DECL + + result = entries["result"] + parameters = entries["parameters"] + p_types = entries["p_types"] + p_outs = entries["p_outs"] + trace = entries["trace"] + container = function.semantic_parent + code = """%MethodCode +{} +""".format(trace) + if function.is_pure_virtual_method(): + code += """ bool sipSelfWasArg = false; +""" + # + # Generate parameter list, wrapping shared data as needed. + # + sip_stars = [] + for i, parameter_h in enumerate(parameters): + tmp, p = parameter_h.cxx_to_cxx("a{}".format(i), p_types[i], p_outs[i]) + code += tmp + sip_stars.append(p) + # + # Generate function call, unwrapping shared data result as needed. + # + # TODO: When should the logic be bracketed by Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS + # versus the form: + # + # try + # { + # stuff; + # } + # catch(...) + # { + # sipRaiseUnknownException(); + # return NULL; + # } + # + # is it to do with exception handling support in SIP? + # + if function.kind == CursorKind.CONSTRUCTOR: + fn = fqn(function, "")[:-2].replace("::", "_") + callsite = """ Py_BEGIN_ALLOW_THREADS + sipCpp = new sip{fn}({args}); + Py_END_ALLOW_THREADS +""" + callsite = callsite.replace("{fn}", fn) + callsite = callsite.replace("{args}", ", ".join(sip_stars)) + code += callsite + elif function.spelling.startswith("operator") and \ + function.spelling.endswith(("*=", "/=", "%=", "+=", "-=", ">>=", "<<=", "&=", "^=", "|=")): + # + # This is a compound assignment. + # + callsite = """ Py_BEGIN_ALLOW_THREADS + sipCpp->{fn}({args}); + Py_END_ALLOW_THREADS +""" + callsite = callsite.replace("{fn}", function.spelling) + callsite = callsite.replace("{args}", ", ".join(sip_stars)) + code += callsite + else: + fn = function.spelling + if function.is_static_method() or not in_class(function): + fn = fqn(function) + callsite = """ cxxvalue = {fn}({args}); +""" + elif function.access_specifier == AccessSpecifier.PROTECTED: + if function.is_virtual_method(): + callsite = """#if defined(SIP_PROTECTED_IS_PUBLIC) + cxxvalue = sipSelfWasArg ? sipCpp->{qn}{fn}({args}) : sipCpp->{fn}({args}); +#else + cxxvalue = sipCpp->sipProtectVirt_{fn}(sipSelfWasArg{sep}{args}); +#endif +""" + if parameters: + callsite = callsite.replace("{sep}", ", ", 1) + else: + callsite = callsite.replace("{sep}", "", 1) + callsite = callsite.replace("{qn}", fqn(function, "")) + else: + callsite = """#if defined(SIP_PROTECTED_IS_PUBLIC) + cxxvalue = sipCpp->{fn}({args}); +#else + cxxvalue = sipCpp->sipProtect_{fn}({args}); +#endif +""" + else: + if function.is_virtual_method(): + callsite = """ cxxvalue = sipSelfWasArg ? sipCpp->{qn}{fn}({args}) : sipCpp->{fn}({args}); +""" + callsite = callsite.replace("{qn}", fqn(function, "")) + else: + callsite = """ cxxvalue = sipCpp->{fn}({args}); +""" + callsite = callsite.replace("{fn}", fn) + callsite = callsite.replace("{args}", ", ".join(sip_stars)) + code += """ Py_BEGIN_ALLOW_THREADS +""" + if result.category == HeldAs.VOID: + code += callsite.replace("cxxvalue = ", "") + else: + code += """ typedef {} CxxvalueT; +""".format(entries["cxx_fn_result"]) + code += callsite.replace("cxxvalue = ", "CxxvalueT cxxvalue = ") + code += result.declare_type_helpers("sipRes", "return 0;") + code += result.cxx_to_py("sipRes", False, "cxxvalue") + code += """ Py_END_ALLOW_THREADS +""" + code += """ +%End +""" + if function.is_virtual_method(): + code += """%VirtualCatcherCode +{}""".format(trace) + # + # Generate parameter list, unwrapping shared data as needed. + # + sip_stars = [] + sip_encodings = [] + for i, parameter_h in enumerate(parameters): + p = "a{}".format(i) + code += parameter_h.declare_type_helpers(p, "sipIsErr = 1;") + code += parameter_h.cxx_to_py(p, False, p) + + # + # Encoding map. + # + _encoding_map = { + HeldAs.BYTE: "c", + HeldAs.INTEGER: "n", + HeldAs.FLOAT: "d", + HeldAs.POINTER: "N", + HeldAs.OBJECT: "N", + } + _cast_map = { + HeldAs.BYTE: "(char)cxx{}", + HeldAs.INTEGER: "(long long)cxx{}", + HeldAs.FLOAT: "(double)cxx{}", + HeldAs.POINTER: "cxx{}, gen{}T, NULL", + HeldAs.OBJECT: "&cxx{}, gen{}T, NULL", + } + e = _encoding_map[parameter_h.category] + v = _cast_map[parameter_h.category].format(p, p) + sip_stars.append(v) + sip_encodings.append(e) + sip_stars = ['"' + "".join(sip_encodings) + '"'] + sip_stars + code += """ + PyObject *result = sipCallMethod(&sipIsErr, sipMethod, {}, NULL); + if (result == NULL) {{ + sipIsErr = 1; + + // TBD: Free all the pyobjects. + }}""".format(", ".join(sip_stars)) + if result.category == HeldAs.VOID: + pass + else: + code += """ else { + // Convert the result to the C++ type. TBD: Figure out type encodings? + sipParseResult(&sipIsErr, sipMethod, result, "i", &sipRes); + Py_DECREF(result); + }""" + code += """ +%End +""" + return code + + def analyse_function(self, rule, cursor, sip): + """ + Analyse a function, and return the results via the sip. + + :param rule: The caller asking for the template expansion. + :param cursor: The CursorKind for whom the expansion is being performed. + This is the function whose parameters and/or return type + uses templates. + :param sip: The sip. Expected keys: + + decl Optional. Name of the function. + foo dd + """ + annotations = initialise_cxx_decl(sip) + # + # Deal with function result. + # + result_h = self.return_helper(sip["fn_result"], cursor.result_type.get_canonical()) + sip["fn_result"] = result_h.py_fn_result(cursor.kind == CursorKind.CONSTRUCTOR) + # + # Now the function parameters. + # + clang_types = [c.type.get_canonical() for c in cursor.get_children() if c.kind == CursorKind.PARM_DECL] + parameters = [] + p_types = [] + p_outs = [] + # + # Generate parameter list, unwrapping shared data as needed. + # + for i, p in enumerate(sip["cxx_parameters"]): + # + # Get to type by removing any default value then stripping the name. + # + lhs, rhs = RE_PARAMETER_VALUE.split(p + "=")[:2] + t, v = RE_PARAMETER_TYPE.match(lhs).groups() + t = t.strip() + parameter_h = self.parameter_helper(t, clang_types[i]) + sip["parameters"][i] = parameter_h.py_parameter(t, v, rhs, annotations[i]) + parameters.append(parameter_h) + p_types.append(t) + p_outs.append("Out" in annotations[i]) + # + # Run the template handler... + # + trace = trace_generated_for(cursor, rule, {"{}({})".format(result_h.cxx_t, result_h.category): + ["{}({})".format(p.cxx_t, p.category) for p in parameters]}) + entries = { + "result": result_h, + "parameters": parameters, + "p_types": p_types, + "p_outs": p_outs, + "trace": trace, + "cxx_fn_result": sip["cxx_fn_result"], + } + return entries + + +def function_uses_templates(container, function, sip, rule): + """ + A FunctionDb-compatible function used to create %MethodCode expansions + for C++ functions with templated return types and/or parameters. + + Rule writers can tailor the expansion using custom subclasses of + FunctionExpander, FunctionParameterHelper and FunctionReturnHelper + passed via sip attributes: + + - "template" Provide the overall template. + - "parameter_helper" Tailored parameter handling. + - "return_helper" Tailored return value handling. + """ + # + # No generated code for signals, SIP does not support that. + # + if sip["is_signal"]: + logger.warning( + _("SIP does not support templated signals: {}").format(utils.item_describe(function))) + return rule_helpers.SILENT_NOOP + template = sip.get("template", FunctionExpander) + parameter_helper = sip.get("parameter_helper", FunctionParameterHelper) + return_helper = sip.get("return_helper", FunctionReturnHelper) + template = template(parameter_helper, return_helper) + entries = template.analyse_function(rule, function, sip) + code = template.expand_template(function, entries) + sip["code"] = code Index: find-modules/module_generation/templates/std_n_boost.py =================================================================== --- /dev/null +++ find-modules/module_generation/templates/std_n_boost.py @@ -0,0 +1,403 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +SIP binding code for std'n'boost-template classes. The main content is: + + - FunctionDb, ParameterDb and TypeCodeDb-compatible entries usable in + RuleSets (e.g. list_{fn_result,parameter,typecode}). + + - PointerExpander class which implements shared_ptr templates. + +This is supported by other public methods and classes which can be used as +examples and/or helper code. +""" +import gettext +import logging +import os +import re + +import builtin_rules +from utils import HeldAs +from clangcparser import TypeKind +import templates.mappedtype +import templates.methodcode + +gettext.install(os.path.basename(__file__)) +logger = logging.getLogger(__name__) + +# Keep PyCharm happy. +_ = _ + +TYPE = "type" +ARGS = "args" +KNOWN_PTRS = "(?P<" + TYPE + ">(boost::shared_ptr|std::auto_ptr))" +RE_KNOWN_PTRS = re.compile("(const )?" + KNOWN_PTRS + "<(?P<" + ARGS + ">.*)>( [&*])?") + +RE_LIST_T = "(const )?(std::vector)<(.*)>( [&*])?" +RE_PTRS_T = "(const )?" + KNOWN_PTRS + "<(.*)>( [&*])?" + +RE_LIST_V = RE_LIST_T + ".*" +RE_PTRS_V = RE_PTRS_T + ".*" + +RE_UNTEMPLATED_FN = ".*[^>]" + + +class DictHelperKey(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, py_v, needs_reference, cxx_v): + cxx_v += ".key()" + return super(DictHelperKey, self).cxx_to_py(py_v, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(DictHelperKey, self).py_to_cxx(name, needs_reference, py_v) + + +class DictHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, py_v, needs_reference, cxx_v): + cxx_v += ".value()" + return super(DictHelperValue, self).cxx_to_py(py_v, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(DictHelperValue, self).py_to_cxx(name, needs_reference, py_v) + + +class ListHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, py_v, needs_reference, cxx_v): + cxx_v += "->at(i)" + return super(ListHelperValue, self).cxx_to_py(py_v, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(ListHelperValue, self).py_to_cxx(name, needs_reference, py_v) + + +class SetHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py(self, py_v, needs_reference, cxx_v): + cxx_v = "*" + cxx_v + return super(SetHelperValue, self).cxx_to_py(py_v, needs_reference, cxx_v) + + def py_to_cxx(self, name, needs_reference, py_v): + return super(SetHelperValue, self).py_to_cxx(name, needs_reference, py_v) + + +class FunctionParameterHelper(templates.methodcode.FunctionParameterHelper): + """ + Automatic handling for templated function parameter types with auto-unwrapping + of KNOWN_PTRS. + """ + def __init__(self, cxx_t, clang_t, manual_t=None): + is_pointer = RE_KNOWN_PTRS.match(cxx_t) + if is_pointer: + template_type = is_pointer.group(TYPE) + template_args = [is_pointer.group(ARGS)] + cxx_t = template_args[0] + " *" + super(FunctionParameterHelper, self).__init__(cxx_t, clang_t, manual_t) + self.is_pointer = is_pointer + + def cxx_to_py(self, py_v, needs_reference, cxx_v): + if self.is_pointer: + cxx_v += ".get()" + return super(FunctionParameterHelper, self).cxx_to_py(py_v, needs_reference, cxx_v) + + def cxx_to_cxx(self, aN, original_type, is_out_paramter): + if self.is_pointer: + code = """ typedef """ + HeldAs.base_type(original_type) + """ Cxx{aN}T; + Cxx{aN}T *cxx{aN} = new Cxx{aN}T({aN}); +""" + code = code.replace("{aN}", aN, 4) + # + # A QWeakPointer has to be constructed via an intermediate QSharedPointer. + # + is_weakptr = re.match("(const )?QWeakPointer<(.*)>( .)?", original_type) + if is_weakptr: + code = code.replace("{aN}", "QSharedPointer<" + is_weakptr.group(2) + ">(" + aN + ")") + else: + code = code.replace("{aN}", aN) + aN = "*cxx" + aN + return code, aN + return super(FunctionParameterHelper, self).cxx_to_cxx(aN, original_type, is_out_paramter) + + def py_parameter(self, type_, name, default, annotations): + if self.is_pointer and default: + # + # TODO: We really just want default.data() as the default value, but SIP gets confused. + # + default = "NULL" + return super(FunctionParameterHelper, self).py_parameter(type_, name, default, annotations) + + +class FunctionReturnHelper(templates.methodcode.FunctionReturnHelper): + """ + Automatic handling for templated function return types with auto-unwrapping + of KNOWN_PTRS templates. + """ + def __init__(self, cxx_t, clang_t, manual_t=None): + is_pointer = RE_KNOWN_PTRS.match(cxx_t) + if is_pointer: + template_type = is_pointer.group(TYPE) + template_args = [is_pointer.group(ARGS)] + cxx_t = template_args[0] + " *" + super(FunctionReturnHelper, self).__init__(cxx_t, clang_t, manual_t) + self.is_pointer = is_pointer + + def cxx_to_py(self, py_v, needs_reference, cxx_v): + if self.is_pointer: + cxx_v += ".get()" + return super(FunctionReturnHelper, self).cxx_to_py(py_v, needs_reference, cxx_v) + + def py_fn_result(self, is_constructor): + if self.is_pointer: + return self.cxx_t + return super(FunctionReturnHelper, self).py_fn_result(is_constructor) + + +class ListExpander(templates.mappedtype.ListExpander): + + def __init__(self, value_helper=ListHelperValue): + super(ListExpander, self).__init__(value_helper) + + def header_for(self, template_t): + """ + There is no simple algorithmic way to map template_t to a header file. + """ + headers = { + "std::vector": "vector", + } + return headers[template_t] + + def parse_template(self, template, expected): + """ + std::vector takes a second argument, which is the allocator. + """ + return super(ListExpander, self).parse_template(template, expected + 1) + + +class PointerExpander(templates.mappedtype.AbstractExpander): + + def __init__(self): + super(PointerExpander, self).__init__([("value", PointerHelperValue)]) + + def expand_generic(self, template_t, entries): + """ + Generic support for template pointer types. The template parameter can + be of any integral (int, long, enum) type or non-integral type, for + example, QWeakPointer. + + :param template_t: The name of the C++ template, e.g. "QSharedDataPointer". + :param entries: Dictionary describing the C++ template. Expected keys: + + value Is the value integral, pointer or object? + """ + value_h = entries["value"] + code = """ +%TypeHeaderCode +#include <{header_h}> +%End +%ConvertFromTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;") + code += """ + // Convert the value from C++. +""" + code += value_h.cxx_to_py("value", True, "sipCpp") + code += """ if (value == NULL) { + PyErr_Format(PyExc_TypeError, "cannot convert value"); + return 0; + } + return value; +%End +%ConvertToTypeCode +""" + code += value_h.declare_type_helpers("value", "return 0;", need_string=value_h.category != HeldAs.INTEGER) + code += """ PyObject *value; + + // Convert the value to C++. + value = sipPy; +""" + code += value_h.py_to_cxx("value", True, "value") + code += """ + if (*sipIsErr) { +""" + if value_h.category != HeldAs.INTEGER: + code += """ if (cxxvalue == NULL) { + PyErr_Format(PyExc_TypeError, "value has type '%s' but '%s' is expected", + Py_TYPE(value)->tp_name, cxxvalueS); + } +""" + code += value_h.release_sip_helper("value") + code += """ return 0; + } +""" + code += value_h.release_sip_helper("value") + code += """ *sipCppPtr = new {template_t}(cxxvalue); +""" + code += """ return sipGetState(sipTransferObj); +%End +""" + code = code.replace("{header_h}", self.header_for(template_t), 1) + code = code.replace("{template_t}", template_t) + code = code.replace("{sip_t}", value_h.sip_t) + return code + + def header_for(self, template_t): + """ + There is no simple algorithmic way to map template_t to a header file. + """ + headers = { + "std::auto_ptr": "memory", + "boost::shared_ptr": os.path.join("boost", "shared_ptr.hpp"), + } + return headers[template_t] + + +class PointerHelperValue(templates.mappedtype.GenerateMappedHelper): + def cxx_to_py_value(self, name, cxx_v, transfer): + """An expression converting the named C++ value to Python.""" + cxx_v += "->get()" + code = "sipConvertFromType((void *){cxx_v}, gen{name}T, sipTransferObj)" + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + return code + + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + code = """ PyObject *{name} = {cxx_to_py_value}; +""" + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + code = code.replace("{cxx_to_py_value}", cxx_to_py_value) + return code + + +def function_uses_templates(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create %MethodCode expansions + for C++ functions with templated return types and/or parameters. + """ + sip.setdefault("parameter_helper", FunctionParameterHelper) + sip.setdefault("return_helper", FunctionReturnHelper) + builtin_rules.function_uses_templates(container, fn, sip, rule) + + +def list_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python lists. + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = ListExpander() + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def list_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python lists. + """ + template = ListExpander() + template.expand(rule, parameter, sip["decl"], sip) + + +def list_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for C++ + types with one template argument into Python lists. + """ + template = ListExpander() + template.expand(rule, typedef, sip["decl"], sip) + + +def pointer_fn_result(container, fn, sip, rule): + """ + A FunctionDb-compatible function used to create a %MappedType for a + boost pointer type. + + A call to function_uses_templates handles the %MethodCode expansion. + """ + template = PointerExpander() + template.expand(rule, fn, sip["fn_result"], sip) + function_uses_templates(container, fn, sip, rule) + + +def pointer_parameter(container, fn, parameter, sip, rule): + """ + A ParameterDb-compatible function used to create a %MappedType for a + boost pointer type. + """ + template = PointerExpander() + template.expand(rule, parameter, sip["decl"], sip) + + +def pointer_typecode(container, typedef, sip, rule): + """ + A TypeCodeDb-compatible function used to create a %MappedType for a + boost pointer type. + """ + template = PointerExpander() + if typedef.underlying_type.kind == TypeKind.ELABORATED: + # + # This is a typedef of a typedef, and Clang gets the template + # parameters wrong, so abort. + # + return + assert typedef.underlying_type.kind == TypeKind.UNEXPOSED + template.expand(rule, typedef, sip["decl"], sip) + + +def function_rules(): + return [ + # + # Supplement std'n'boost templates with %MappedTypes for the function + # result, and call function_uses_templates for %MethodCode too. + # + [".*", RE_UNTEMPLATED_FN, "", RE_LIST_V, ".*", list_fn_result], + [".*", RE_UNTEMPLATED_FN, "", RE_PTRS_V, ".*", pointer_fn_result], + # + # Call function_uses_templates...the parameters have been dealt with elsewhere. + # + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_LIST_V, function_uses_templates], + [".*", RE_UNTEMPLATED_FN, "", ".*", RE_PTRS_V, function_uses_templates], + ] + + +def parameter_rules(): + return [ + # + # Supplement std'n'boost templates with %MappedTypes. + # + [".*", RE_UNTEMPLATED_FN, ".*", RE_LIST_V, ".*", list_parameter], + [".*", RE_UNTEMPLATED_FN, ".*", RE_PTRS_V, ".*", pointer_parameter], + ] + + +def typedef_rules(): + return [ + # + # Supplement std'n'boost templates with %MappedTypes. + # + [".*", ".*", ".*", RE_LIST_T, list_typecode], + [".*", ".*", ".*", RE_PTRS_T, pointer_typecode], + ] Index: find-modules/module_generation/tests/Rules/CMakeLists.txt =================================================================== --- /dev/null +++ find-modules/module_generation/tests/Rules/CMakeLists.txt @@ -0,0 +1,185 @@ +# Copyright 2017 Shaheed Haque +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# This is a helper script used to locate binaries, libraries and other +# platform-dependent items for use by the Python code. The output is +# NOT CMakeCache.txt et. al. but a JSON file: +# +# { +# "CXX_SOURCES": { +# "KF5Activities": { +# "LIBRARIES": "/usr/lib/x86_64-linux-gnu/libKF5Activities.so.5.31.0", +# "INCLUDE_DIRS": "/usr/include/KF5/KActivities;/usr/include/KF5" +# }, +# ... +# "$": null +# }, +# "CXX_DEPENDENCIES": { +# "Qt5Widgets": { +# "LIBRARIES": ..., +# "INCLUDE_DIRS": ..., +# }, +# "$": null +# }, +# "CXX_COMPILE_OPTIONS": "-fPIC,-std=gnu++14", +# "SIP_DEPENDENCIES": "/usr/share/sip/PyQt5", +# "CXX_SOURCE_ROOT": "/usr/include/KF5", +# "PY_PACKAGE": "PyKF5", +# "$": null +# } +# +# The "$" dict entries are a convenience which the caller discards. +# + +cmake_minimum_required(VERSION 3.7) + +project(PySample) + +set(CXX_DEPENDENCIES Qt5Core) + +# +# Find the list of targets for a Qt component. +# +function(get_qt5_targets component) + string(REPLACE "Qt5" "Qt5::" target ${component}) + if(NOT TARGET ${target}) + message(STATUS "Ignoring invalid target \"${target}\" for ${component}") + set(target "") + endif() + set(targets "${target}" PARENT_SCOPE) +endfunction(get_qt5_targets) + +# +# Fetch a target property, recursing if necessary. +# +function(get_target_property_recursive target property) + set(result) + get_target_property(values ${target} ${property}) + if(values STREQUAL "values-NOTFOUND") + # Skip + message(STATUS "Warning: Target ${target} has no property ${property}") + else() + foreach(value ${values}) + string(FIND ${value} "$ + # + string(REGEX REPLACE "\\$" "\\1" nested_tgt ${value}) + string(REGEX REPLACE "\\$" "\\2" nested_prop ${value}) + get_target_property_recursive(${nested_tgt} ${nested_prop}) + list(APPEND result ${get_target_property_recursive_result}) + else() + list(APPEND result ${value}) + endif() + endforeach() + endif() + set(get_target_property_recursive_result "${result}" PARENT_SCOPE) +endfunction(get_target_property_recursive) + +# +# Find the includes, libraries etc. for a component. +# +function(get_targets_info dict component targets) + if(targets STREQUAL "") + message(STATUS "Warning: No targets for ${component}") + return() + endif() + # + # Make a combined list of includes, libraries etc. + # + set(libraries) + set(includes) + foreach(target ${targets}) + if(TARGET ${target}) + get_target_property(tmp ${target} LOCATION) + list(APPEND libraries ${tmp}) + get_target_property_recursive(${target} INTERFACE_INCLUDE_DIRECTORIES) + list(APPEND includes ${get_target_property_recursive_result}) + else() + message(STATUS "Warning: Ignoring invalid target \"${target}\" in ${f}") + endif() + endforeach() + # + # De-duplicate and write results. + # + if(DEFINED includes) + list(REMOVE_DUPLICATES includes) + # + # Not sure why the headers seem to include this. + # + list(REMOVE_ITEM includes "/usr/include") + endif() + file(APPEND "${dict}" "\"${component}\": {\n") + file(APPEND "${dict}" " \"LIBRARIES\": \"${libraries}\",\n") + file(APPEND "${dict}" " \"INCLUDE_DIRS\": \"${includes}\"\n") + file(APPEND "${dict}" "},\n") + set(includes "${includes}" PARENT_SCOPE) +endfunction(get_targets_info) + +find_package(ECM REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${ECM_MODULE_PATH}) +find_package(PyQt REQUIRED) +include(FeatureSummary) +# +# Write the values we found as a JSON blob. +# +set(dict ${CMAKE_CURRENT_BINARY_DIR}/configure.json) +message(STATUS "Writing ${dict}") +file(WRITE "${dict}" "{\n") +# +# Sources. +# +file(APPEND "${dict}" "\"CXX_SOURCES\": {\n") +foreach(component ${CXX_SOURCES}) +endforeach() +set(CXX_SOURCE_ROOT) +# +# Write results. +# +file(APPEND "${dict}" "\"$\": null\n") +file(APPEND "${dict}" "},\n") +# +# Dependencies. +# +file(APPEND "${dict}" "\"CXX_DEPENDENCIES\": {\n") +foreach(component ${CXX_DEPENDENCIES}) + find_package(${component}) + get_qt5_targets(${component}) + get_targets_info(${dict} ${component} "${targets}") +endforeach() +file(APPEND "${dict}" "\"$\": null\n") +file(APPEND "${dict}" "},\n") +file(APPEND "${dict}" "\"CXX_COMPILE_OPTIONS\": \"-fPIC,-std=gnu++14\",\n") +file(APPEND "${dict}" "\"SIP_DEPENDENCIES\": \"${PyQt_INCLUDE_DIRS}\",\n") +file(APPEND "${dict}" "\"CXX_SOURCE_ROOT\": \"${CXX_SOURCE_ROOT}\",\n") +file(APPEND "${dict}" "\"SIP_PACKAGE\": \"${CMAKE_PROJECT_NAME}\",\n") +file(APPEND "${dict}" "\"$\": null\n") +file(APPEND "${dict}" "}\n") +unset(component) +unset(dict) Index: find-modules/module_generation/tests/Rules/FindPyQt.cmake =================================================================== --- /dev/null +++ find-modules/module_generation/tests/Rules/FindPyQt.cmake @@ -0,0 +1 @@ +../../PyKF5/FindPyQt.cmake \ No newline at end of file Index: find-modules/module_generation/tests/Rules/__init__.py =================================================================== --- /dev/null +++ find-modules/module_generation/tests/Rules/__init__.py @@ -0,0 +1,324 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +""" +SIP binding customisation for tests. +""" +import os + +import builtin_rules +import rule_helpers +import rules_engine + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) + + +def container_discard_templated_bases(container, sip, matcher): + sip["base_specifiers"] = [] + + +def container_needs_typedef(container, sip, rule): + rule_helpers.container_add_typedefs(container, sip, rule, "QMap") + + +def container_emit_modulecode(container, sip, matcher): + container_discard_templated_bases(container, sip, matcher) + sip["modulecode"][sip["name"]] = """ +%ModuleHeaderCode +#define ModuleCodeTypeCheck 1 +%End +""" + + +def function_emit_modulecode(container, function, sip, matcher): + sip["modulecode"][sip["name"]] = """ +%ModuleHeaderCode +#define ModuleCodeFunctionCheck 1 +%End +""" + sip["code"] = """ +%MethodCode +#ifndef ModuleCodeTypeCheck +#error Could not find ModuleCodeTypeCheck +#endif +#ifndef ModuleCodeTypedefCheck +#error Could not find ModuleCodeTypedefCheck +#endif +#ifndef ModuleCodeFunctionCheck +#error Could not find ModuleCodeFunctionCheck +#endif +#ifndef ModuleCodeParameterCheck +#error Could not find ModuleCodeParameterCheck +#endif +%End +""" + + +def fn_cxx_decl(container, function, sip, matcher): + rule_helpers.initialise_cxx_decl(sip) + + +def parameter_emit_modulecode(container, function, parameter, sip, matcher): + sip["modulecode"][sip["name"]] = """ +%ModuleHeaderCode +#define ModuleCodeParameterCheck 1 +%End +""" + + +def parameter_in_out(container, function, parameter, sip, matcher): + rule_helpers.parameter_out(container, function, parameter, sip, matcher) + rule_helpers.parameter_in(container, function, parameter, sip, matcher) + + +def typedef_emit_modulecode(container, typedef, sip, matcher): + sip["modulecode"][sip["name"]] = """ +%ModuleHeaderCode +#define ModuleCodeTypedefCheck 1 +%End +""" + + +def methodGenerator(function, sip, entry): + sip["code"] = """ + %MethodCode + sipRes = {} + myAcumulate(a0); + %End + """.format(sip["ctx"]) + + +def container_rules(): + return [ + # + # Discard Qt metatype system. + # + [".*", "(QMetaTypeId|QTypeInfo)", ".*", ".*", ".*", rule_helpers.container_discard], + [".*", "Shared", ".*", ".*", ".*", rule_helpers.container_discard_QSharedData_base], + [".*", "TemplateDerivative", ".*", ".*", ".*", container_discard_templated_bases], + [".*", "ModuleCodeType", ".*", ".*", ".*", container_emit_modulecode], + [".*", "ObscureSyntax", ".*", ".*", ".*", container_needs_typedef], + ] + + +def forward_declaration_rules(): + return [ + [".*", "ExternalFwdDecl", ".*", rule_helpers.forward_declaration_mark_external], + ] + + +def function_rules(): + return [ + # + # Discard functions emitted by QOBJECT. + # + [".*", "metaObject|qt_metacast|tr|trUtf8|qt_metacall|qt_check_for_QOBJECT_macro", ".*", ".*", ".*", rule_helpers.function_discard], + # + # SIP does not support operator=. + # + [".*", "operator=", ".*", ".*", ".*", rule_helpers.function_discard], + # + # TODO: Temporarily remove any functions which require std templates. + # + [".*", ".*", ".*", ".*", ".*std::function.*", rule_helpers.function_discard], + [".*", ".*", ".*", ".*", ".*std::numeric_limits.*", rule_helpers.function_discard], + ["TypedefUser", "setTagPattern", ".*", ".*", ".*", rule_helpers.function_discard], + ["Sample1_1", "markedInOutCxxDecl", ".*", ".*", ".*", fn_cxx_decl], + ] + + +def parameter_rules(): + return [ + ["Sample1_1", "markedInOut.*", "scursor", ".*", ".*", parameter_in_out], + ["Sample1_1", "markedInOut.*", "result", ".*", ".*", rule_helpers.parameter_out], + ] + + +def typedef_rules(): + return [] + + +def unexposed_rules(): + return [] + + +def variable_rules(): + return [ + # + # Discard variable emitted by QOBJECT. + # + [".*", "staticMetaObject", ".*", rule_helpers.variable_discard], + ] + + +def methodcode(): + return { + "SomeNS": { + "customMethod": { + "code": """ + %MethodCode + sipRes = myAcumulate(a0); + %End + """ + } + }, + "ObscureSyntax": { + "defaultsAndParameterTemplate": { + "code": """ + %MethodCode + sipRes = ObscureSyntax::INCORRECT; + if ((*a0 == Qt::MatchWrap) && + (*a1 == Qt::MatchFlags(Qt::MatchStartsWith | Qt::MatchWrap)) && + (*a2 == (Qt::MatchStartsWith | Qt::MatchWrap)) && + (a3 == 1) && + (a4 == 2) && + (a5 == 2) && + (a6 == ObscureSyntax::INCORRECT) && + (a7 == MyObject::Val2) && + (a8->isEmpty()) && + (a9 != NULL)) { + sipRes = ObscureSyntax::CORRECT; + } + %End + """, + "cxx_decl": [ + "Qt::MatchFlags flagsOne", + "Qt::MatchFlags flagsMultiple", + "int simple", + "int complex", + "int brackets", + "LocalEnum anEnum", + "QMap chachacha" + ], + "cxx_fn_result": "int", + }, + "returnTemplate": { + "code": """ + %MethodCode + sipRes = sipCpp->returnTemplate(); + sipRes->insert("foo", ObscureSyntax::CORRECT); + %End + """, + "cxx_decl": [ + ], + "cxx_fn_result": "QMap *", + }, + }, + "cpplib.h": { + "anotherCustomMethod": { + "code": methodGenerator, + "ctx": 42 + } + } + } + + +def modulecode(): + return { + "CppLib/cpplib.h": { + "code": """ +%ModuleHeaderCode +int myAcumulate(const QList *list); +%End\n +%ModuleCode +int myAcumulate(const QList *list) { + return std::accumulate(list->begin(), list->end(), 0); +} +%End\n""" + } + + } + + +def typecode(): + return {} + + +class RuleSet(rules_engine.RuleSet): + """ + SIP file generator rules. This is a set of (short, non-public) functions + and regular expression-based matching rules. + """ + def __init__(self): + super(RuleSet, self).__init__( + container_rules=container_rules, forward_declaration_rules=forward_declaration_rules, + function_rules=function_rules, parameter_rules=parameter_rules, typedef_rules=typedef_rules, + unexposed_rules=unexposed_rules, variable_rules=variable_rules, methodcode=methodcode, + modulecode=modulecode, typecode=typecode) + self.pd_cache = None + + def _fill_cache(self): + if self.pd_cache is None: + self.pd_cache = rules_engine.get_platform_dependencies(os.path.dirname(os.path.realpath(__file__))) + + def _update_dir_set(self, result, key1, key2): + self._fill_cache() + for component, data in self.pd_cache[key1].items(): + dirlist = data[key2].split(";") + dirlist = [os.path.normpath(i) for i in dirlist if i] + result.update(dirlist) + + def cxx_source_root(self): + return os.path.join(os.path.dirname(SCRIPT_DIR), "sources") + + def cxx_sources(self): + return [] + + def cxx_includes(self): + source_root = self.cxx_source_root() + os.path.sep + result = set() + self._update_dir_set(result, "CXX_DEPENDENCIES", "INCLUDE_DIRS") + # + # We include anything which is not under the source root: those are dependencies too! + # + self._update_dir_set(result, "CXX_SOURCES", "INCLUDE_DIRS") + result = [i for i in result if not i.startswith(source_root)] + result = sorted(result) + return result + + def cxx_compile_flags(self): + QT5_COMPILE_FLAGS = ["-fPIC", "-std=gnu++14"] + return QT5_COMPILE_FLAGS + + def cxx_libraries(self): + result = set() + self._update_dir_set(result, "CXX_SOURCES", "LIBRARIES") + self._update_dir_set(result, "CXX_DEPENDENCIES", "LIBRARIES") + result = [i for i in result] + result = sorted(result) + return result + + def sip_package(self): + self._fill_cache() + return self.pd_cache["SIP_PACKAGE"] + + def sip_imports(self): + self._fill_cache() + result = set() + dirlist = self.pd_cache["SIP_DEPENDENCIES"].split(";") + result.update(dirlist) + result = [i for i in result if i] + result = sorted(result) + return result Index: find-modules/module_generation/tests/sources/CppLib/cpplib.h =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/CppLib/cpplib.h @@ -0,0 +1 @@ +../../../../../tests/GenerateSipBindings/cpplib.h \ No newline at end of file Index: find-modules/module_generation/tests/sources/CppLib/cpplib.cpp =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/CppLib/cpplib.cpp @@ -0,0 +1 @@ +../../../../../tests/GenerateSipBindings/cpplib.cpp \ No newline at end of file Index: find-modules/module_generation/tests/sources/CppLib/subdir =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/CppLib/subdir @@ -0,0 +1 @@ +../../../../../tests/GenerateSipBindings/subdir \ No newline at end of file Index: find-modules/module_generation/tests/sources/ExternalLib/external_lib.h =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/ExternalLib/external_lib.h @@ -0,0 +1 @@ +../../../../../tests/GenerateSipBindings/external_lib.h \ No newline at end of file Index: find-modules/module_generation/tests/sources/ExternalLib/external_lib.cpp =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/ExternalLib/external_lib.cpp @@ -0,0 +1 @@ +../../../../../tests/GenerateSipBindings/external_lib.cpp \ No newline at end of file Index: find-modules/module_generation/tests/sources/clang_torture.h =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/clang_torture.h @@ -0,0 +1,58 @@ +// +// Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +// Syntax exerciser for Clang front end. +#pragma once + +enum GlobalEnum +{ + GLOBAL_1, +}; + +class ParameterPassing +{ +public: + enum NestedEnum + { + NESTED_1, + }; + + ParameterPassing(int wo_default, const int const_int = 0, int w_default = 1, GlobalEnum g = GLOBAL_1, + NestedEnum n = NESTED_1) + { + wo_default = wo_default; + wo_default = w_default; + wo_default = const_int; + auto gg = g; + auto nn = n; + } + + void fn_1(const char *&star_and, const char *star) + { + star_and = star; + } +}; Index: find-modules/module_generation/tests/sources/sample1.h =================================================================== --- /dev/null +++ find-modules/module_generation/tests/sources/sample1.h @@ -0,0 +1,110 @@ +// +// Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef SAMPLE1_H +#define SAMPLE1_H + +class QString +{ +public: + QString() {}; +}; + +/** + * https://www.riverbankcomputing.com/pipermail/pyqt/2017-May/039159.html + */ +class Sample1_1 +{ +public: + bool simple(const char *&scursor, const char* send, QString &result, bool allow8Bit = false) + { + scursor = send; + result = QString(); + return allow8Bit; + } + + bool markedInOut(const char *&scursor, const char* send, QString &result, bool allow8Bit = false) + { + return simple(scursor, send, result, allow8Bit); + } + + bool markedInOutCxxDecl(const char *&scursor, const char* send, QString &result, bool allow8Bit = false) + { + return markedInOut(scursor, send, result, allow8Bit); + } +}; + +/** + * https://www.riverbankcomputing.com/pipermail/pyqt/2017-May/039219.html + */ +template +class MyTemplate +{ + int x; +public: + MyTemplate() { x = U; }; + T *fn() { return nullptr; }; +}; + +namespace OuterNamespace +{ + template + class MyTemplate + { + public: + MyTemplate() {}; + T *fn() { return nullptr; }; + }; +}; + +class OuterClass +{ +public: + template + class MyTemplate + { + public: + MyTemplate() {}; + T fn() { return Z; }; + }; +}; + +/** + * https://www.riverbankcomputing.com/pipermail/pyqt/2017-June/039293.html + * https://www.riverbankcomputing.com/pipermail/pyqt/2017-June/039311.html + */ +class PrivateDestructor +{ +public: + PrivateDestructor(char *foo = "A quoted stri\\ng with \"quotes\"") {}; +private: + virtual ~PrivateDestructor() {}; +private: + PrivateDestructor(const PrivateDestructor &) {}; +}; + +#endif Index: find-modules/module_generation/tests/test_00_generate_and_compile.py =================================================================== --- /dev/null +++ find-modules/module_generation/tests/test_00_generate_and_compile.py @@ -0,0 +1,79 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +from __future__ import print_function +import os +import re +import shutil +import tempfile + +import module_compiler +import module_generator + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +TMP_DIR = None +BUILD_DIR = None +COMPILE_DIR = None + + +class Test: + @classmethod + def setup_class(cls): + global TMP_DIR + TMP_DIR = tempfile.mkdtemp() + global BUILD_DIR + BUILD_DIR = os.path.join(TMP_DIR, "tmp") + global COMPILE_DIR + COMPILE_DIR = os.path.join(TMP_DIR, "tmp2") + print("TMP_DIR = " + TMP_DIR) + + @classmethod + def teardown_class(cls): + pass#shutil.rmtree(TMP_DIR) + + def test_00_generate(self): + """ + test_00_generate: Generate module. + """ + rules_pkg = os.path.join(SCRIPT_DIR, "Rules") + d = module_generator.ModuleGenerator(rules_pkg, BUILD_DIR) + omit = re.compile(".*cpp$", re.I) + select = re.compile(".*", re.I) + attempts, failures, directories = d.process_tree(jobs=0, selector=select, omitter=omit) + print("Summary: {} processing errors for {} files in {} modules".format(len(failures), attempts, directories)) + assert len(failures) == 0, "{}".format(failures) + + def test_10_compile(self): + """ + test_10_compile: Compile module. + """ + rules_pkg = os.path.join(SCRIPT_DIR, "Rules") + d = module_compiler.ModuleCompiler(rules_pkg, True, BUILD_DIR, COMPILE_DIR) + omit = re.compile("=Nothing=", re.I) + select = re.compile(".*", re.I) + attempts, failures = d.process_tree(jobs=0, selector=select, omitter=omit) + print("Summary: {} processing errors for {} modules".format(len(failures), attempts)) + assert len(failures) == 0, "{}".format(failures) Index: find-modules/module_generation/tests/test_clang.py =================================================================== --- /dev/null +++ find-modules/module_generation/tests/test_clang.py @@ -0,0 +1,63 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +from __future__ import print_function + +import clangcplus + + +class Test: + def test_custom_parser(self): + """ + test_custom_parser: Generate custom parser. + """ + class MyCursor(clangcplus.Cursor): + CLASS_MAP = {} + + class MyTranslationUnitCursor(clangcplus.TranslationUnitCursor, MyCursor): + pass + + class MyStruct(MyCursor): + CURSOR_KINDS = [clangcplus.CursorKind.STRUCT_DECL] + + def __init__(self, container): + proxy_attributes = [] + super(MyStruct, self).__init__(container, proxy_attributes) + + # + # Ensure the base parser has not been affected. + # + self.test_base_parser() + assert MyTranslationUnitCursor in MyCursor.CLASS_MAP.values() + assert MyStruct in MyCursor.CLASS_MAP.values() + assert MyTranslationUnitCursor in MyTranslationUnitCursor.CLASS_MAP.values() + + def test_base_parser(self): + """ + test_base_parser: Test base parser. + """ + assert clangcplus.TranslationUnitCursor in clangcplus.Cursor.CLASS_MAP.values() + assert clangcplus.TranslationUnitCursor in clangcplus.TranslationUnitCursor.CLASS_MAP.values() Index: find-modules/module_generation/utils.py =================================================================== --- /dev/null +++ find-modules/module_generation/utils.py @@ -0,0 +1,452 @@ +# +# Copyright 2017 by Shaheed Haque (srhaque@theiet.org) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +"""Utility functions.""" +import os +import re + +from clangcplus import CursorKind, TypeKind + + +def decompose_template(text): + """ + Any template parameters from the parent in "type_text" are extracted, and + return (name, [parameters]) or (name, None). + """ + parameters = text.split("<", 1)[-1] + if parameters == text: + parameters = None + else: + # + # Strip any suffix &* and parameter name. + # + tmp = parameters.rsplit(">", 1)[0] + # + # Parse... + # + parameters = [] + bracket_level = 0 + left = 0 + for right, token in enumerate(tmp): + if bracket_level <= 0 and token is ",": + parameters.append(tmp[left:right].strip()) + left = right + 1 + elif token is "<": + bracket_level += 1 + elif token is ">": + bracket_level -= 1 + parameters.append(tmp[left:].strip()) + # + # Strip any keywords before the name. + # + name = text.split("<", 1)[0] + while name.startswith(("const ", "volatile ", "typename ", "class ", "struct ")): + tmp, name = name.split(None, 1) + return name, parameters + + +def decompose_type(text): + prefixes = [] + while text.startswith(("const ", "volatile ")): + tmp, text = text.split(None, 1) + prefixes.append(tmp + " ") + suffixes = [] + while text.endswith("]"): + text, tmp = text.rsplit("[", 1) + suffixes.insert(0, "[" + tmp) + # + # Check for const array elements. + # + if text.endswith("const "): + text, tmp = text[:-6], "const " + suffixes.insert(0, tmp) + operators = [] + while text.endswith(("&&", "&", "*")): + if text.endswith("&&"): + operators.append("&&") + text = text[:-2] + if text.endswith("&"): + operators.append("&") + text = text[:-1] + if text.endswith("*"): + operators.append("*") + text = text[:-1] + text = text.rstrip() + return prefixes, text, operators, suffixes + + +def fqn(cursor, alternate_spelling=None): + """ + A handy helper to return the fully-qualified name for something. + """ + parents = "" + parent = cursor.semantic_parent + while parent and parent.kind != CursorKind.TRANSLATION_UNIT: + if parent.kind != CursorKind.UNEXPOSED_DECL: + parents = parent.spelling + "::" + parents + parent = parent.semantic_parent + if alternate_spelling is None: + text = cursor.spelling + else: + text = alternate_spelling + return parents + text + + +def cursor_parents(cursor): + """ + A helper function which returns the parents of a cursor in the forms: + + - A::B::C::...N for non-top level entities. + - filename.h for top level entities. + - "" in exceptional cases of having no parents. + """ + parents = "" + parent = cursor.semantic_parent + while parent and parent.kind != CursorKind.TRANSLATION_UNIT: + parents = parent.spelling + "::" + parents + parent = parent.semantic_parent + if parent and not parents: + return os.path.basename(parent.spelling) + return parents[:-2] + + +def item_describe(item, alternate_spelling=None): + """ + A helper function providing a standardised description for an item, + which may be a cursor. + """ + if isinstance(item, str): + return item + if alternate_spelling is None: + text = item.spelling + else: + text = alternate_spelling + return "{} on line {} '{}::{}'".format(item.kind.name, item.extent.start.line, cursor_parents(item), text) + + +def trace_inserted_for(item, rule): + trace = "// Inserted for {} (by {}):\n".format(item_describe(item), rule) + return trace + + +def trace_discarded_by(item, rule): + trace = "// Discarded {} (by {})\n".format(item_describe(item), rule) + return trace + + +def trace_generated_for(item, rule, extra): + trace = "// Generated for {} (by {}): {}\n".format(item_describe(item), rule, extra) + return trace + + +def trace_modified_by(item, rule): + trace = "// Modified {} (by {}):\n".format(item_describe(item), rule) + return trace + + +class HeldAs(object): + """ + Items are held either as integral values, pointer values or objects. The + Python interchange logic depends on this. + + TODO: In the case of pointers, we also need to know whether a specialised + pointer type is in use (or just "*"). + """ + VOID = "void" + BYTE = "BYTE" + INTEGER = "INTEGER" + FLOAT = "FLOAT" + POINTER = "POINTER" + OBJECT = "OBJECT" + RE_BYTE = re.compile("^char\W|\Wchar\W|\Wchar$|^char$") + RE_INTEGER = re.compile("^(short|int|long)\W|\W(short|int|long)\W|\W(short|int|long)$|^(short|int|long)$|^bool$") + RE_FLOAT = re.compile("^(float|double)\W|\W(float|double)\W|\W(float|double)$|^(float|double)$") + # + # Clang primitive classifications corresponding to Python types. + # + _fwd_map = { + BYTE: [TypeKind.CHAR_S, TypeKind.CHAR_U, TypeKind.SCHAR, TypeKind.UCHAR], + INTEGER: [TypeKind.BOOL, TypeKind.CHAR16, TypeKind.CHAR32, + TypeKind.USHORT, TypeKind.UINT, TypeKind.ULONG, TypeKind.ULONGLONG, TypeKind.UINT128, + TypeKind.SHORT, TypeKind.INT, TypeKind.LONG, TypeKind.LONGLONG, TypeKind.INT128, + TypeKind.ENUM], + FLOAT: [TypeKind.FLOAT, TypeKind.DOUBLE, TypeKind.FLOAT128, TypeKind.LONGDOUBLE], + POINTER: [TypeKind.POINTER, TypeKind.MEMBERPOINTER], + OBJECT: [TypeKind.RECORD], + } + _rev_map = None + + def __init__(self, cxx_t, clang_t, base_cxx_t=None): + """ + :param cxx_t: The declaration text from the source code. + :param clang_t: The Clang type or None. + :param base_cxx_t: The base type, can be manually overridden. + """ + self.cxx_t = cxx_t + if base_cxx_t is None: + base_cxx_t = self.base_type(cxx_t) + # + # This may be a mapped type. + # + self.is_mapped_type = ("<" in base_cxx_t) + if self.is_mapped_type: + self.sip_t = "sipFindType(cxx{name}S)" + else: + # + # Primitive types don't need help from a sipType_xxx. + # + base_held_as = HeldAs.categorise(base_cxx_t, clang_t) + if base_held_as in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + self.sip_t = base_held_as + else: + self.sip_t = "sipType_" + base_cxx_t.replace("::", "_") + self.category = HeldAs.categorise(cxx_t, clang_t) + + def cxx_to_py_template(self, name, cxx_v, cxx_to_py_value): + raise NotImplementedError() + + def cxx_to_py_value(self, name, cxx_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + PyLong_FromLong((long){cxx_v}) +#else + PyInt_FromLong((long){cxx_v}) +#endif +)""", + HeldAs.FLOAT: + "PyFloat_FromDouble((double){cxx_v})", + HeldAs.POINTER: + "sipConvertFromType((void *)cxx{name}, gen{name}T, sipTransferObj)", + HeldAs.OBJECT: + "sipConvertFromNewType((void *)cxx{name}, gen{name}T, sipTransferObj)", + } + ptr_options = { + HeldAs.BYTE: + "PyString_FromStringAndSize((char *)({cxx_v}), 1)", + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + PyLong_FromLong((long)*{cxx_v}) +#else + PyInt_FromLong((long)*{cxx_v}) +#endif +)""", + HeldAs.FLOAT: + "PyFloat_FromDouble((double)*({cxx_v}))", + } + + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{cxx_v}", cxx_v) + return code + + def py_to_cxx_template(self, name, py_v, py_to_cxx_value): + raise NotImplementedError() + + def py_to_cxx_value(self, name, py_v, transfer): + """An expression converting the named C++ value to Python.""" + options = { + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong({py_v}) +#else + (Cxx{name}T)PyInt_AsLong({py_v}) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble({py_v})", + HeldAs.POINTER: + "reinterpret_cast(sipForceConvertToType({py_v}, gen{py_v}T, sipTransferObj, SIP_NOT_NONE, &{py_v}State, sipIsErr))", + HeldAs.OBJECT: + "reinterpret_cast(sipForceConvertToType({py_v}, gen{py_v}T, sipTransferObj, SIP_NOT_NONE, &{py_v}State, sipIsErr))", + } + ptr_options = { + HeldAs.BYTE: + "(Cxx{name}T)PyString_AsString({py_v})", + HeldAs.INTEGER: + """( +#if PY_MAJOR_VERSION >= 3 + (Cxx{name}T)PyLong_AsLong(*({py_v})) +#else + (Cxx{name}T)PyInt_AsLong(*({py_v})) +#endif +)""", + HeldAs.FLOAT: + "(Cxx{name}T)PyFloat_AsDouble(*({py_v}))", + } + + if self.category == HeldAs.POINTER and self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + code = ptr_options[self.sip_t] + else: + code = options[self.category] + code = code.replace("{name}", name) + code = code.replace("{py_v}", py_v) + return code + + @staticmethod + def actual_type(parameter_text): + """ + :param parameter_text: The text from the source code. + :return: the type of the parameter. + """ + return parameter_text + + @staticmethod + def base_type(parameter_text): + """ + :param parameter_text: The text from the source code. + :return: the base_type of the parameter, e.g. without a pointer suffix. + """ + prefixes, name, operators, suffixes = decompose_type(parameter_text) + return name + + @staticmethod + def categorise(cxx_t, clang_t): + """ + We would like to be able to use clang type system to determine the + HELD_AS, but this is not always possible. For example, in a templated + typedef, any int types are not even included in the children of the + typedef. Similarly, in a variable declaration, a templated type might + appear simply as TypeKind.UNEXPOSED. + + TODO: When we figure this out, get rid of the horrid heuristics. + + :param cxx_t: The text from the source code. + :param clang_t: The Clang type or None. + :return: the storage type of the object. + """ + if cxx_t.endswith("*"): + return HeldAs.POINTER + if cxx_t.endswith("&"): + return HeldAs.categorise(cxx_t[:-1].strip(), clang_t) + if cxx_t == HeldAs.VOID: + return HeldAs.VOID + if cxx_t.startswith("type-parameter-"): + return HeldAs.OBJECT + if "<" in cxx_t: + return HeldAs.OBJECT + if HeldAs.RE_BYTE.search(cxx_t): + return HeldAs.BYTE + # + # Must check for FLOAT before INTEGER in case of "long double" etc. + # + if HeldAs.RE_FLOAT.search(cxx_t): + return HeldAs.FLOAT + if HeldAs.RE_INTEGER.search(cxx_t): + return HeldAs.INTEGER + # + # The clang type should be authoritative. + # + if clang_t is not None: + # + # One-time init of lookup map. + # + if not HeldAs._rev_map: + HeldAs._rev_map = {} + for key, values in HeldAs._fwd_map.items(): + for value in values: + HeldAs._rev_map[value] = key + # + # Lookup. + # + try: + return HeldAs._rev_map[clang_t.kind] + except KeyError: + if clang_t.kind == TypeKind.VOID: + return HeldAs.VOID + if clang_t.kind in [TypeKind.LVALUEREFERENCE, TypeKind.TYPEDEF]: + return HeldAs.categorise(cxx_t, clang_t.underlying_type.get_canonical()) + # + # We we already know it did not seem to be a pointer, so check for a templated object: + # + # TypeKind.ELABORATED: 'WTF::Vector' + # TypeKind.UNEXPOSED: 'HashMap >' + # + if "<" in cxx_t and clang_t.kind in [TypeKind.ELABORATED, TypeKind.UNEXPOSED]: + return HeldAs.OBJECT + return HeldAs.OBJECT + return HeldAs.OBJECT + + def declare_type_helpers(self, name, error, need_string=False, need_cxx_t=True): + """ + Make it easier to track changes in generated output. + + By creating local definitions at the top of each section of emitted output, the rest of the expanded template + is a constant. This makes it easier to see the effect of any changes using "diff". + """ + code = "" + if self.is_mapped_type or need_string: + code += """ const char *cxx{name}S = "{cxx_t}"; +""" + if need_cxx_t: + code += """ typedef {cxx_t} Cxx{name}T; +""" + if self.category in [HeldAs.POINTER, HeldAs.OBJECT]: + # + # If the sipTypeDef needs a run-time lookup using sipFindMappedType, can convert that into a one-off cost + # using a static. + # + if self.is_mapped_type: + code += """ static const sipTypeDef *gen{name}T = NULL; + if (gen{name}T == NULL) { + gen{name}T = {sip_t}; + if (gen{name}T == NULL) { + PyErr_Format(PyExc_TypeError, "cannot find SIP type for '%s'", cxx{name}S); + {error} + } + } +""" + elif self.sip_t in [HeldAs.BYTE, HeldAs.INTEGER, HeldAs.FLOAT]: + # + # Primitive types don't need help from a sipType_xxx. + # + pass + else: + code += """ const sipTypeDef *gen{name}T = {sip_t}; +""" + code = code.replace("{sip_t}", self.sip_t) + code = code.replace("{name}", name) + code = code.replace("{error}", error) + code = code.replace("{cxx_t}", self.cxx_t) + return code + + def cxx_to_py(self, name, needs_reference, cxx_v): + cxx_to_py_value = self.cxx_to_py_value(name, cxx_v, "sipTransferObj" if needs_reference else "NULL") + code = self.cxx_to_py_template(name, cxx_v, cxx_to_py_value) + return code + + def py_to_cxx(self, name, needs_reference, py_v): + py_to_cxx_value = self.py_to_cxx_value(name, py_v, "sipTransferObj" if needs_reference else "NULL") + code = self.py_to_cxx_template(name, py_v, py_to_cxx_value) + return code