buildlog-consultant_0.0.18.orig/.flake80000644000000000000000000000017014010634306014706 0ustar00[flake8] extend-ignore = E203, E266, E501, W293, W291 max-line-length = 88 max-complexity = 18 select = B,C,E,F,W,T4,B9 buildlog-consultant_0.0.18.orig/.github/0000755000000000000000000000000014010642564015102 5ustar00buildlog-consultant_0.0.18.orig/.gitignore0000644000000000000000000000007414027657552015546 0ustar00update.sh buildlog_consultant.egg-info __pycache__ *~ build buildlog-consultant_0.0.18.orig/AUTHORS0000644000000000000000000000004314011353417014604 0ustar00Jelmer Vernooij buildlog-consultant_0.0.18.orig/CODE_OF_CONDUCT.md0000644000000000000000000000642314015600254016341 0ustar00# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at team@dulwich.io. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq buildlog-consultant_0.0.18.orig/LICENSE0000644000000000000000000004325414011353417014554 0ustar00 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, 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. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, 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 or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's 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 give any other recipients of the Program a copy of this License along with the Program. 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 Program or any portion of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, 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 Program, 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 Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) 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; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, 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 executable. However, as a special exception, the source code 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. If distribution of executable or 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 counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program 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. 5. 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 Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program 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 to this License. 7. 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 Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program 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 Program. 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. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program 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. 9. The Free Software Foundation may publish revised and/or new versions of the 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 Program 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 Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, 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 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 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 Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. 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 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. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. buildlog-consultant_0.0.18.orig/MANIFEST.in0000644000000000000000000000012114033106535015270 0ustar00include CODE_OF_CONDUCT.md include AUTHORS include README.md include SECURITY.md buildlog-consultant_0.0.18.orig/Makefile0000644000000000000000000000016314106242630015176 0ustar00all: check flake8 mypy check: python3 setup.py test flake8: flake8 mypy: python3 -m mypy buildlog_consultant buildlog-consultant_0.0.18.orig/PKG-INFO0000644000000000000000000000057214205553356014651 0ustar00Metadata-Version: 2.1 Name: buildlog-consultant Version: 0.0.18 Summary: buildlog parser and analyser Home-page: https://github.com/jelmer/buildlog-consultant Author: Jelmer Vernooij Author-email: jelmer@jelmer.uk License: UNKNOWN Project-URL: Repository, https://github.com/jelmer/buildlog-consultant.git Platform: UNKNOWN License-File: LICENSE License-File: AUTHORS UNKNOWN buildlog-consultant_0.0.18.orig/README.md0000644000000000000000000000033514162106315015017 0ustar00The build log consultant can parse and analyse build log files. Currently supported container formats: * sbuild * plain For a longer introduction, see the [blog post](https://www.jelmer.uk/buildlog-consultant.html). buildlog-consultant_0.0.18.orig/SECURITY.md0000644000000000000000000000045614015600253015332 0ustar00# Security Policy ## Supported Versions buildlog-consultant is still under heavy development. Only the latest version is security supported. ## Reporting a Vulnerability Please report security issues by e-mail to jelmer@jelmer.uk, ideally PGP encrypted to the key at https://jelmer.uk/D729A457.asc buildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/0000755000000000000000000000000014016462165021312 5ustar00buildlog-consultant_0.0.18.orig/buildlog_consultant/0000755000000000000000000000000014006376323017617 5ustar00buildlog-consultant_0.0.18.orig/releaser.conf0000644000000000000000000000055114027657552016227 0ustar00name: "buildlog-consultant" timeout_days: 5 tag_name: "v$VERSION" verify_command: "python3 setup.py test" update_version { path: "setup.py" match: "^ version=\"(.*)\",$" new_line: " version=\"$VERSION\"," } update_version { path: "buildlog_consultant/__init__.py" match: "^__version__ = \\((.*)\\)" new_line: "__version__ = $TUPLED_VERSION" } buildlog-consultant_0.0.18.orig/setup.cfg0000644000000000000000000000004614016462165015366 0ustar00[egg_info] tag_build = tag_date = 0 buildlog-consultant_0.0.18.orig/setup.py0000755000000000000000000000150614205553356015267 0ustar00#!/usr/bin/python3 from setuptools import setup setup( name="buildlog-consultant", packages=[ "buildlog_consultant", "buildlog_consultant.tests", ], version="0.0.18", author="Jelmer Vernooij", author_email="jelmer@jelmer.uk", url="https://github.com/jelmer/buildlog-consultant", description="buildlog parser and analyser", project_urls={ "Repository": "https://github.com/jelmer/buildlog-consultant.git", }, test_suite="buildlog_consultant.tests.test_suite", install_requires=['python_debian', 'PyYAML', 'requirements-parser'], entry_points={ 'console_scripts': [ ('analyse-sbuild-log=' 'buildlog_consultant.sbuild:main'), ('analyse-build-log=' 'buildlog_consultant.common:main'), ], }, ) buildlog-consultant_0.0.18.orig/.github/workflows/0000755000000000000000000000000014010642564017137 5ustar00buildlog-consultant_0.0.18.orig/.github/workflows/pythonpackage.yml0000644000000000000000000000210014205553356022516 0ustar00name: Python package on: push: pull_request: schedule: - cron: '0 6 * * *' # Daily 6AM UTC build jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] python-version: [3.7, 3.8, 3.9] fail-fast: false steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip flake8 cython git+https://salsa.debian.org/python-debian-team/python-debian python setup.py develop - name: Style checks run: | python -m flake8 - name: Typing checks run: | pip install -U mypy types-PyYAML python -m mypy buildlog_consultant || true if: "matrix.python-version != 'pypy3'" - name: Test suite run run: | python -m unittest buildlog_consultant.tests.test_suite env: PYTHONHASHSEED: random buildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/PKG-INFO0000644000000000000000000000057214205553356022416 0ustar00Metadata-Version: 2.1 Name: buildlog-consultant Version: 0.0.18 Summary: buildlog parser and analyser Home-page: https://github.com/jelmer/buildlog-consultant Author: Jelmer Vernooij Author-email: jelmer@jelmer.uk License: UNKNOWN Project-URL: Repository, https://github.com/jelmer/buildlog-consultant.git Platform: UNKNOWN License-File: LICENSE License-File: AUTHORS UNKNOWN buildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/SOURCES.txt0000644000000000000000000000140514162106315023170 0ustar00.flake8 .gitignore AUTHORS CODE_OF_CONDUCT.md LICENSE MANIFEST.in Makefile README.md SECURITY.md releaser.conf setup.py .github/workflows/pythonpackage.yml buildlog_consultant/__init__.py buildlog_consultant/apt.py buildlog_consultant/autopkgtest.py buildlog_consultant/common.py buildlog_consultant/sbuild.py buildlog_consultant.egg-info/PKG-INFO buildlog_consultant.egg-info/SOURCES.txt buildlog_consultant.egg-info/dependency_links.txt buildlog_consultant.egg-info/entry_points.txt buildlog_consultant.egg-info/requires.txt buildlog_consultant.egg-info/top_level.txt buildlog_consultant/tests/__init__.py buildlog_consultant/tests/test_apt.py buildlog_consultant/tests/test_autopkgtest.py buildlog_consultant/tests/test_common.py buildlog_consultant/tests/test_sbuild.pybuildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/dependency_links.txt0000644000000000000000000000000114016462165025360 0ustar00 buildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/entry_points.txt0000644000000000000000000000017414027657552024622 0ustar00[console_scripts] analyse-build-log = buildlog_consultant.common:main analyse-sbuild-log = buildlog_consultant.sbuild:main buildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/requires.txt0000644000000000000000000000005114032410672023700 0ustar00PyYAML python_debian requirements-parser buildlog-consultant_0.0.18.orig/buildlog_consultant.egg-info/top_level.txt0000644000000000000000000000002414016462165024040 0ustar00buildlog_consultant buildlog-consultant_0.0.18.orig/buildlog_consultant/__init__.py0000644000000000000000000000663014205553356021741 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # encoding: utf-8 # # 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 from dataclasses import dataclass from typing import List __version__ = (0, 0, 18) version_string = '.'.join(map(str, __version__)) problem_clses = {} class Problem(object): kind: str is_global: bool = False def json(self): raise NotImplementedError(self.json) def problem(kind, is_global=False): def json(self): ret = {} for name in self.__dataclass_fields__: ret[name] = getattr(self, name) return ret @classmethod def from_json(cls, data): return cls(**data) def _wrap(cls): ret = dataclass(cls) ret.kind = kind ret.is_global = is_global if not hasattr(ret, 'json'): ret.json = json if not hasattr(ret, 'from_json'): ret.from_json = from_json problem_clses[ret.kind] = ret return ret return _wrap class Match: line: str lines: List[str] class SingleLineMatch(Match): offset: int line: str def __init__(self, offset: int, line: str): self.offset = offset self.line = line def __repr__(self): return "%s(%r, %r)" % (type(self).__name__, self.offset, self.line) def __eq__(self, other): return ( isinstance(self, type(other)) and self.offset == other.offset and self.line == other.line ) @property def lines(self) -> List[str]: return [self.line] @property def linenos(self) -> List[int]: return [self.lineno] @property def offsets(self) -> List[int]: return [self.offset] @property def lineno(self) -> int: return self.offset + 1 @classmethod def from_lines(cls, lines, offset): return cls(offset, lines[offset]) class MultiLineMatch(Match): offsets: List[int] lines: List[str] def __init__(self, offsets: List[int], lines: List[str]): self.offsets = offsets self.lines = lines def __repr__(self): return "%s(%r, %r)" % (type(self).__name__, self.offsets, self.lines) def __eq__(self, other): return ( isinstance(self, type(other)) and self.offsets == other.offsets and self.lines == other.lines ) @property def line(self): return self.lines[-1] @property def offset(self): return self.offsets[-1] @property def lineno(self): return self.offset + 1 @property def linenos(self): return [o + 1 for o in self.offsets] @classmethod def from_lines(cls, lines, offsets): return cls(offsets, [lines[o] for o in offsets]) buildlog-consultant_0.0.18.orig/buildlog_consultant/apt.py0000644000000000000000000002632714060733076020771 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # encoding: utf-8 # # 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 import re from debian.changelog import Version from debian.deb822 import PkgRelation from typing import List, Optional, Tuple import yaml from . import Problem, SingleLineMatch, problem from .common import NoSpaceOnDevice class DpkgError(Problem): kind = "dpkg-error" def __init__(self, error): self.error = error def __eq__(self, other): return isinstance(other, type(self)) and self.error == other.error def __str__(self): return "Dpkg Error: %s" % self.error def __repr__(self): return "%s(%r)" % (type(self).__name__, self.error) class AptUpdateError(Problem): """Apt update error.""" kind = "apt-update-error" class AptFetchFailure(AptUpdateError): """Apt file fetch failed.""" kind = "apt-file-fetch-failure" def __init__(self, url, error): self.url = url self.error = error def __eq__(self, other): if not isinstance(other, type(self)): return False if self.url != other.url: return False if self.error != other.error: return False return True def __str__(self): return "Apt file fetch error: %s" % self.error class AptMissingReleaseFile(AptUpdateError): kind = "missing-release-file" def __init__(self, url): self.url = url def __eq__(self, other): if not isinstance(other, type(self)): return False if self.url != self.url: return False return True def __str__(self): return "Missing release file: %s" % self.url class AptPackageUnknown(Problem): kind = "apt-package-unknown" def __init__(self, package): self.package = package def __eq__(self, other): return isinstance(other, type(self)) and self.package == other.package def __str__(self): return "Unknown package: %s" % self.package def __repr__(self): return "%s(%r)" % (type(self).__name__, self.package) class AptBrokenPackages(Problem): kind = "apt-broken-packages" def __init__(self, description): self.description = description def __str__(self): return "Broken apt packages: %s" % (self.description,) def __repr__(self): return "%s(%r)" % (type(self).__name__, self.description) def __eq__(self, other): return isinstance(other, type(self)) and self.description == other.description def find_apt_get_failure(lines): # noqa: C901 """Find the key failure line in apt-get-output. Returns: tuple with (match, error object) """ ret = (None, None) OFFSET = 50 for i in range(1, OFFSET): lineno = len(lines) - i if lineno < 0: break line = lines[lineno].strip("\n") if line.startswith("E: Failed to fetch "): m = re.match("^E: Failed to fetch ([^ ]+) (.*)", line) if m: if "No space left on device" in m.group(2): problem = NoSpaceOnDevice() else: problem = AptFetchFailure(m.group(1), m.group(2)) return SingleLineMatch.from_lines(lines, lineno), problem return SingleLineMatch.from_lines(lines, lineno), None if line in ( "E: Broken packages", "E: Unable to correct problems, you have held broken " "packages.", ): error = AptBrokenPackages(lines[lineno - 1].strip()) return SingleLineMatch.from_lines(lines, lineno - 1), error m = re.match("E: The repository '([^']+)' does not have a Release file.", line) if m: return SingleLineMatch.from_lines(lines, lineno), AptMissingReleaseFile( m.group(1) ) m = re.match( "dpkg-deb: error: unable to write file '(.*)': " "No space left on device", line, ) if m: return SingleLineMatch.from_lines(lines, lineno), NoSpaceOnDevice() m = re.match(r"E: You don't have enough free space in (.*)\.", line) if m: return SingleLineMatch.from_lines(lines, lineno), NoSpaceOnDevice() if line.startswith("E: ") and ret[0] is None: ret = SingleLineMatch.from_lines(lines, lineno), None m = re.match(r"E: Unable to locate package (.*)", line) if m: return SingleLineMatch.from_lines(lines, lineno), AptPackageUnknown( m.group(1) ) if line == "E: Write error - write (28: No space left on device)": return SingleLineMatch.from_lines(lines, lineno), NoSpaceOnDevice() m = re.match(r"dpkg: error: (.*)", line) if m: if m.group(1).endswith(": No space left on device"): return SingleLineMatch.from_lines(lines, lineno), NoSpaceOnDevice() return SingleLineMatch.from_lines(lines, lineno), DpkgError(m.group(1)) m = re.match(r"dpkg: error processing package (.*) \((.*)\):", line) if m: return ( SingleLineMatch.from_lines(lines, lineno + 1), DpkgError("processing package %s (%s)" % (m.group(1), m.group(2))), ) for i, line in enumerate(lines): m = re.match( r" cannot copy extracted data for '(.*)' to " r"'(.*)': failed to write \(No space left on device\)", line, ) if m: return SingleLineMatch.from_lines(lines, lineno), NoSpaceOnDevice() m = re.match(r" .*: No space left on device", line) if m: return SingleLineMatch.from_lines(lines, i), NoSpaceOnDevice() return ret def find_apt_get_update_failure(sbuildlog): focus_section = "update chroot" lines = sbuildlog.get_section_lines(focus_section) match, error = find_apt_get_failure(lines) return focus_section, match, error def find_cudf_output(lines): for i in range(len(lines) - 1, 0, -1): if lines[i].startswith("output-version: "): break else: return None output = [] while lines[i].strip(): output.append(lines[i]) i += 1 return yaml.safe_load("\n".join(output)) try: from typing import TypedDict except ImportError: # python < 3.9 from typing import Dict, Any ParsedRelation = Dict[str, Dict[str, Any]] else: ParsedRelation = TypedDict( "ParsedRelation", { "name": str, "archqual": Optional[str], "version": Optional[Tuple[str, str]], "arch": Optional[List["PkgRelation.ArchRestriction"]], "restrictions": Optional[List[List["PkgRelation.BuildRestriction"]]], }, ) @problem("unsatisfied-apt-dependencies") class UnsatisfiedAptDependencies: relations: List[List[List[ParsedRelation]]] def __str__(self): return "Unsatisfied APT dependencies: %s" % PkgRelation.str(self.relations) @classmethod def from_str(cls, text): return cls(PkgRelation.parse_relations(text)) @classmethod def from_json(cls, data): relations = [] for relation in data['relations']: sub = [] for entry in relation: pkg = { 'name': entry['name'], 'archqual': entry.get('archqual'), 'arch': entry.get('arch'), 'restrictions': entry.get('restrictions'), 'version': (entry['version'][0], Version(entry['version'][1])) if entry['version'] else None, } sub.append(pkg) relations.append(sub) return cls(relations=relations) def __repr__(self): return "%s.from_str(%r)" % ( type(self).__name__, PkgRelation.str(self.relations), ) @problem("unsatisfied-apt-conflicts") class UnsatisfiedAptConflicts: relations: List[List[List[ParsedRelation]]] def __str__(self): return "Unsatisfied APT conflicts: %s" % PkgRelation.str(self.relations) def error_from_dose3_report(report): def fixup_relation(rel): for o in rel: for d in o: if d['version']: try: newoperator = {'<': '<<', '>': '>>'}[d['version'][0]] except KeyError: pass else: d['version'] = (newoperator, d['version'][1]) packages = [entry["package"] for entry in report] assert packages == ["sbuild-build-depends-main-dummy"] if report[0]["status"] != "broken": return None missing = [] conflict = [] for reason in report[0]["reasons"]: if "missing" in reason: relation = PkgRelation.parse_relations( reason["missing"]["pkg"]["unsat-dependency"] ) fixup_relation(relation) missing.extend(relation) if "conflict" in reason: relation = PkgRelation.parse_relations( reason["conflict"]["pkg1"]["unsat-conflict"] ) fixup_relation(relation) conflict.extend(relation) if missing: return UnsatisfiedAptDependencies(missing) if conflict: return UnsatisfiedAptConflicts(conflict) def find_install_deps_failure_description(sbuildlog) -> Tuple[Optional[str], Optional[SingleLineMatch], Optional[Problem]]: error = None DOSE3_SECTION = "install dose3 build dependencies (aspcud-based resolver)" dose3_lines = sbuildlog.get_section_lines(DOSE3_SECTION) if dose3_lines: dose3_output = find_cudf_output(dose3_lines) if dose3_output: error = error_from_dose3_report(dose3_output["report"]) return DOSE3_SECTION, None, error SECTION = "install package build dependencies" build_dependencies_lines = sbuildlog.get_section_lines(SECTION) if build_dependencies_lines: dose3_output = find_cudf_output(build_dependencies_lines) if dose3_output: error = error_from_dose3_report(dose3_output["report"]) return SECTION, None, error match, error = find_apt_get_failure(build_dependencies_lines) return SECTION, match, error for section in sbuildlog.sections: if section.title is None: continue if re.match("install (.*) build dependencies.*", section.title.lower()): match, error = find_apt_get_failure(section.lines) if match is not None: return section.title, match, error return section.title, None, error buildlog-consultant_0.0.18.orig/buildlog_consultant/autopkgtest.py0000644000000000000000000005071714050243406022547 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # encoding: utf-8 # # 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 import logging import re from typing import Tuple, Optional, List, Dict, Union from . import Problem, SingleLineMatch, problem from .apt import find_apt_get_failure, AptFetchFailure from .common import find_build_failure_description, ChrootNotFound logger = logging.getLogger(__name__) @problem("badpkg") class AutopkgtestDepsUnsatisfiable: args: List[str] @classmethod def from_blame_line(cls, line): args = [] entries = line[len("blame: ") :].rstrip("\n").split(" ") for entry in entries: try: (kind, arg) = entry.split(":", 1) except ValueError: kind = None arg = entry args.append((kind, arg)) if kind not in ("deb", "arg", "dsc", None): logger.warn("unknown entry %s on badpkg line", entry) return cls(args) @problem("timed-out") class AutopkgtestTimedOut: def __str__(self): return "Timed out" @problem("xdg-runtime-dir-not-set") class XDGRunTimeNotSet: def __str__(self): return "XDG_RUNTIME_DIR is not set" class AutopkgtestTestbedFailure(Problem): kind = "testbed-failure" def __init__(self, reason): self.reason = reason def __eq__(self, other): return type(self) == type(other) and self.reason == other.reason def __repr__(self): return "%s(%r)" % (type(self).__name__, self.reason) def __str__(self): return self.reason class AutopkgtestDepChrootDisappeared(Problem): kind = "testbed-chroot-disappeared" def __init__(self): pass def __str__(self): return "chroot disappeared" def __repr__(self): return "%s()" % (type(self).__name__) def __eq__(self, other): return isinstance(self, type(other)) class AutopkgtestErroneousPackage(Problem): kind = "erroneous-package" def __init__(self, reason): self.reason = reason def __eq__(self, other): return type(self) == type(other) and self.reason == other.reason def __repr__(self): return "%s(%r)" % (type(self).__name__, self.reason) def __str__(self): return self.reason class AutopkgtestStderrFailure(Problem): kind = "stderr-output" def __init__(self, stderr_line): self.stderr_line = stderr_line def __eq__(self, other): return isinstance(self, type(other)) and self.stderr_line == other.stderr_line def __repr__(self): return "%s(%r)" % (type(self).__name__, self.stderr_line) def __str__(self): return "output on stderr: %s" % self.stderr_line def parse_autopgktest_line(line: str) -> Union[str, Tuple[str, Union[Tuple[str, ...]]]]: m = re.match(r"autopkgtest \[([0-9:]+)\]: (.*)", line) if not m: return line timestamp = m.group(1) message = m.group(2) if message.startswith("@@@@@@@@@@@@@@@@@@@@ source "): return (timestamp, ("source",)) elif message.startswith("@@@@@@@@@@@@@@@@@@@@ summary"): return (timestamp, ("summary",)) elif message.startswith("test "): (testname, test_status) = message[len("test ") :].rstrip("\n").split(": ", 1) if test_status == "[-----------------------": return ( timestamp, ( "test", testname, "begin output", ), ) elif test_status == "-----------------------]": return ( timestamp, ( "test", testname, "end output", ), ) elif test_status == (" - - - - - - - - - - results - - - - - - - - - -"): return ( timestamp, ( "test", testname, "results", ), ) elif test_status == (" - - - - - - - - - - stderr - - - - - - - - - -"): return ( timestamp, ( "test", testname, "stderr", ), ) elif test_status == "preparing testbed": return (timestamp, ("test", testname, "prepare testbed")) else: return (timestamp, ("test", testname, test_status)) elif message.startswith("ERROR:"): return (timestamp, ("error", message[len("ERROR: ") :])) else: return (timestamp, (message,)) def parse_autopkgtest_summary(lines): i = 0 while i < len(lines): line = lines[i] m = re.match("([^ ]+)(?:[ ]+)PASS", line) if m: yield i, m.group(1), "PASS", None, [] i += 1 continue m = re.match("([^ ]+)(?:[ ]+)(FAIL|PASS|SKIP|FLAKY) (.+)", line) if not m: i += 1 continue testname = m.group(1) result = m.group(2) reason = m.group(3) offset = i extra = [] if reason == "badpkg": while i + 1 < len(lines) and ( lines[i + 1].startswith("badpkg:") or lines[i + 1].startswith("blame:") ): extra.append(lines[i + 1]) i += 1 yield offset, testname, result, reason, extra i += 1 @problem("testbed-setup-failure") class AutopkgtestTestbedSetupFailure: command: str exit_status: int error: str def __str__(self): return "Error setting up testbed %r failed (%d): %s" % ( self.command, self.exit_status, self.error, ) def find_autopkgtest_failure_description( # noqa: C901 lines: List[str], ) -> Tuple[ Optional[SingleLineMatch], Optional[str], Optional["Problem"], Optional[str] ]: """Find the autopkgtest failure in output. Returns: tuple with (line offset, testname, error, description) """ error: Optional["Problem"] test_output: Dict[Tuple[str, ...], List[str]] = {} test_output_offset: Dict[Tuple[str, ...], int] = {} current_field: Optional[Tuple[str, ...]] = None i = -1 while i < len(lines) - 1: i += 1 line = lines[i] parsed = parse_autopgktest_line(line) if isinstance(parsed, tuple): (timestamp, content) = parsed if content[0] == "test": if content[2] == "end output": current_field = None continue elif content[2] == "begin output": current_field = (content[1], "output") else: current_field = (content[1], content[2]) if current_field in test_output: logger.warn("duplicate output fields for %r", current_field) test_output[current_field] = [] test_output_offset[current_field] = i + 1 elif content == ("summary",): current_field = ("summary",) test_output[current_field] = [] test_output_offset[current_field] = i + 1 elif content[0] == "error": if content[1].startswith('"') and content[1].count('"') == 1: sublines = [content[1]] while i < len(lines): i += 1 sublines += lines[i] if lines[i].count('"') == 1: break content = (content[0], "".join(sublines)) last_test: Optional[str] if current_field is not None: last_test = current_field[0] else: last_test = None msg = content[1] m = re.fullmatch('"(.*)" failed with stderr "(.*)("?)', msg) if m: stderr = m.group(2) m = re.fullmatch( "W: (.*): " "Failed to stat file: No such file or directory", stderr, ) if m: error = AutopkgtestDepChrootDisappeared() return ( SingleLineMatch.from_lines(lines, i), last_test, error, stderr, ) m = re.fullmatch(r"testbed failure: (.*)", msg) if m: testbed_failure_reason = m.group(1) if ( current_field is not None and testbed_failure_reason == "testbed auxverb failed with exit code 255" ): field = (current_field[0], "output") (match, error) = find_build_failure_description( test_output[field] ) if error is not None: assert match is not None return ( SingleLineMatch.from_lines( lines, test_output_offset[field] + match.offset ), last_test, error, match.line, ) if ( testbed_failure_reason == "sent `auxverb_debug_fail', got `copy-failed', " "expected `ok...'" ): (match, error) = find_build_failure_description(lines) if error is not None: return (match, last_test, error, match.line) if ( testbed_failure_reason == "cannot send to testbed: [Errno 32] Broken pipe" ): match, error = find_testbed_setup_failure(lines) if error and match: return (match, last_test, error, match.line) if ( testbed_failure_reason == "apt repeatedly failed to download packages" ): match, error = find_apt_get_failure(lines) if error and match: return (match, last_test, error, match.line) return ( SingleLineMatch.from_lines(lines, i), last_test, AptFetchFailure(None, testbed_failure_reason), None, ) return ( SingleLineMatch.from_lines(lines, i), last_test, AutopkgtestTestbedFailure(testbed_failure_reason), None, ) m = re.fullmatch(r"erroneous package: (.*)", msg) if m: (match, error) = find_build_failure_description(lines[:i]) if error and match: return (match, last_test, error, match.line) return ( SingleLineMatch.from_lines(lines, i), last_test, AutopkgtestErroneousPackage(m.group(1)), None, ) if current_field is not None: match, error = find_apt_get_failure(test_output[current_field]) if ( error is not None and match is not None and current_field in test_output_offset ): return ( SingleLineMatch.from_lines( lines, test_output_offset[current_field] + match.offset ), last_test, error, match.line, ) if msg == "autopkgtest": if lines[i + 1].rstrip() == ": error cleaning up:": return ( SingleLineMatch.from_lines( lines, test_output_offset[current_field] ), last_test, AutopkgtestTimedOut(), lines[i - 1].rstrip(), ) return (SingleLineMatch.from_lines(lines, i), last_test, None, msg) else: if current_field: test_output[current_field].append(line) try: summary_lines = test_output[("summary",)] summary_offset = test_output_offset[("summary",)] except KeyError: while lines and not lines[-1].strip(): lines.pop(-1) if not lines: return (None, None, None, None) else: return ( SingleLineMatch.from_lines(lines, len(lines) - 1), lines[-1], None, None, ) else: for (lineno, testname, result, reason, extra) in parse_autopkgtest_summary( summary_lines ): if result in ("PASS", "SKIP"): continue assert result in ("FAIL", "FLAKY") if reason == "timed out": error = AutopkgtestTimedOut() return ( SingleLineMatch.from_lines(lines, summary_offset + lineno), testname, error, reason, ) elif reason.startswith("stderr: "): output = reason[len("stderr: ") :] stderr_lines = test_output.get((testname, "stderr"), []) stderr_offset = test_output_offset.get((testname, "stderr")) description: Optional[str] if stderr_lines: (match, error) = find_build_failure_description(stderr_lines) if match is not None and stderr_offset is not None: offset = match.offset + stderr_offset description = match.line elif len(stderr_lines) == 1 and re.match( r"QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to \'(.*)\'", stderr_lines[0], ): error = XDGRunTimeNotSet() description = stderr_lines[0] offset = stderr_offset else: if stderr_offset is not None: offset = stderr_offset description = None else: (match, error) = find_build_failure_description([output]) if match is not None: offset = summary_offset + lineno + match.offset description = match.line else: offset = None description = None if offset is None: offset = summary_offset + lineno if error is None: error = AutopkgtestStderrFailure(output) if description is None: description = ( "Test %s failed due to " "unauthorized stderr output: %s" % (testname, error.stderr_line) ) return ( SingleLineMatch.from_lines(lines, offset), testname, error, description, ) elif reason == "badpkg": output_lines = test_output.get((testname, "prepare testbed"), []) output_offset = test_output_offset.get((testname, "prepare testbed")) if output_lines and output_offset: match, error = find_apt_get_failure(output_lines) if error and match: return ( SingleLineMatch.from_lines( lines, match.offset + output_offset ), testname, error, None, ) badpkg = None blame = None for extra_offset, line in enumerate(extra, 1): if line.startswith("badpkg: "): badpkg = line[len("badpkg: ") :] if line.startswith("blame: "): blame = line blame_offset = extra_offset if badpkg is not None: description = "Test %s failed: %s" % (testname, badpkg.rstrip("\n")) else: description = "Test %s failed" % testname error = AutopkgtestDepsUnsatisfiable.from_blame_line(blame) return ( SingleLineMatch.from_lines( lines, summary_offset + lineno + blame_offset ), testname, error, description, ) else: output_lines = test_output.get((testname, "output"), []) output_offset = test_output_offset.get((testname, "output")) (match, error) = find_build_failure_description(output_lines) if match is None or output_offset is None: offset = summary_offset + lineno else: offset = match.offset + output_offset if match is None: description = "Test %s failed: %s" % (testname, reason) else: description = match.line return SingleLineMatch.from_lines(lines, offset), testname, error, description # type: ignore return None, None, None, None def find_testbed_setup_failure(lines): for i in range(len(lines) - 1, 0, -1): line = lines[i] m = re.fullmatch( r"\[(.*)\] failed \(exit status ([0-9]+), stderr \'(.*)\'\)\n", line ) if m: command = m.group(1) status_code = int(m.group(2)) stderr = m.group(3) m = re.fullmatch(r"E: (.*): Chroot not found\\n", stderr) if m: return ( SingleLineMatch.from_lines(lines, i), ChrootNotFound(m.group(1)), ) return ( SingleLineMatch.from_lines(lines, i), AutopkgtestTestbedSetupFailure(command, status_code, stderr), ) m = re.fullmatch( r"\: failure: \[\'(.*)\'\] " r"unexpectedly produced stderr output `(.*)", line, ) if m: command = m.group(1) stderr_output = m.group(2) n = re.match( r"W: /var/lib/schroot/session/(.*): " "Failed to stat file: No such file or directory", stderr_output, ) if n: return ( SingleLineMatch.from_lines(lines, i), AutopkgtestDepChrootDisappeared(), ) return ( SingleLineMatch.from_lines(lines, i), AutopkgtestTestbedSetupFailure(command, 1, stderr_output), ) return None, None buildlog-consultant_0.0.18.orig/buildlog_consultant/common.py0000644000000000000000000037710214205553356021477 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # encoding: utf-8 # # 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 import logging import os import posixpath from typing import List, Optional, Tuple import re import textwrap from . import ( Problem, Match, MultiLineMatch, SingleLineMatch, problem, version_string, ) logger = logging.getLogger(__name__) @problem("missing-python-module") class MissingPythonModule: module: str python_version: Optional[str] = None minimum_version: Optional[str] = None def __str__(self): if self.python_version: ret = "Missing python %d module: " % self.python_version else: ret = "Missing python module: " ret += self.module if self.minimum_version: return ret + " (>= %s)" % self.minimum_version else: return ret def __repr__(self): return "%s(%r, python_version=%r, minimum_version=%r)" % ( type(self).__name__, self.module, self.python_version, self.minimum_version, ) @problem("setuptools-scm-version-issue") class SetuptoolScmVersionIssue: def __str__(self): return "setuptools-scm was unable to find version" @problem("missing-python-distribution") class MissingPythonDistribution: distribution: str python_version: Optional[int] = None minimum_version: Optional[str] = None def __str__(self): if self.python_version: ret = "Missing python %d distribution: " % self.python_version else: ret = "Missing python distribution: " ret += self.distribution if self.minimum_version: return ret + " (>= %s)" % self.minimum_version else: return ret @classmethod def from_requirement_str(cls, text, python_version=None): from requirements.requirement import Requirement req = Requirement.parse(text) if len(req.specs) == 1 and req.specs[0][0] == ">=": return cls(req.name, python_version, req.specs[0][1]) return cls(req.name, python_version) def __repr__(self): return "%s(%r, python_version=%r, minimum_version=%r)" % ( type(self).__name__, self.distribution, self.python_version, self.minimum_version, ) @problem('vcs-control-directory-needed') class VcsControlDirectoryNeeded: vcs: List[str] def __str__(self): return "Version control directory needed" @problem("patch-application-failed") class PatchApplicationFailed: patchname: str def __str__(self): return "Patch application failed: %s" % self.patchname def python_module_not_found(m): try: return MissingPythonModule(m.group(2), python_version=None) except IndexError: return MissingPythonModule(m.group(1), python_version=None) def python_cmd_module_not_found(m): if m.group(1).endswith("python3"): python_version = 3 elif m.group(1).endswith("python2"): python_version = 2 else: python_version = None return MissingPythonModule(m.group(3), python_version=python_version) def python2_reqs_not_found(m): expr = m.group(1) if ">=" in expr: pkg, minimum = expr.split(">=") return MissingPythonModule(pkg.strip(), 2, minimum.strip()) if " " not in expr: return MissingPythonModule(expr, 2) # Hmm return None @problem("missing-vague-dependency") class MissingVagueDependency: name: str url: Optional[str] = None minimum_version: Optional[str] = None def __str__(self): return "Missing dependency: %s" % self.name @problem("missing-qt") class MissingQt: minimum_version: Optional[str] = None def __str__(self): if self.minimum_version: return "Missing QT installation (at least %s)" % ( self.minimum_version) return "Missing QT installation" @problem("missing-x11") class MissingX11: def __str__(self): return "Missing X11 headers" @problem("missing-git-identity") class MissingGitIdentity: def __str__(self): return "Missing Git Identity" @problem("missing-file") class MissingFile: path: str def __str__(self): return "Missing file: %s" % self.path @problem("missing-command-or-build-file") class MissingCommandOrBuildFile: filename: str def __str__(self): return "Missing command or build file: %s" % self.filename @problem("missing-build-file") class MissingBuildFile: filename: str def __str__(self): return "Missing build file: %s" % self.filename def file_not_found(m): if m.group(1).startswith("/") and not m.group(1).startswith("/<>"): return MissingFile(m.group(1)) elif m.group(1).startswith("/<>/"): return MissingBuildFile(m.group(1)[len("/<>/"):]) if '/' not in m.group(1): # Maybe a missing command? return MissingBuildFile(m.group(1)) return None def file_not_found_maybe_executable(m): if m.group(1).startswith("/") and not m.group(1).startswith("/<>"): return MissingFile(m.group(1)) if '/' not in m.group(1): # Maybe a missing command? return MissingCommandOrBuildFile(m.group(1)) return None def webpack_file_missing(m): path = posixpath.join(m.group(2), m.group(1)) if path.startswith("/") and not path.startswith("/<>"): return MissingFile(path) return None @problem("missing-jdk-file") class MissingJDKFile: jdk_path: str filename: str def __str__(self): return "Missing JDK file %s (JDK Path: %s)" % (self.filename, self.jdk_path) @problem("missing-jdk") class MissingJDK: jdk_path: str def __str__(self): return "Missing JDK (JDK Path: %s)" % (self.jdk_path) @problem("missing-jre") class MissingJRE: def __str__(self): return "Missing JRE" def interpreter_missing(m): if m.group(1).startswith("/"): if m.group(1).startswith("/<>"): return None return MissingFile(m.group(1)) if "/" in m.group(1): return None return MissingCommand(m.group(1)) @problem("chroot-not-found") class ChrootNotFound: chroot: str def __str__(self): return "Chroot not found: %s" % self.chroot @problem("missing-sprockets-file") class MissingSprocketsFile: name: str content_type: str def __str__(self): return "Missing sprockets file: %s (type: %s)" % (self.name, self.content_type) @problem("missing-go-package") class MissingGoPackage: package: str def __str__(self): return "Missing Go package: %s" % self.package @problem("missing-c-header") class MissingCHeader: header: str def __str__(self): return "Missing C Header: %s" % self.header @problem("missing-node-module") class MissingNodeModule: module: str def __str__(self): return "Missing Node Module: %s" % self.module @problem("missing-node-package") class MissingNodePackage: package: str def __str__(self): return "Missing Node Package: %s" % self.package def node_module_missing(m): if m.group(1).startswith("/<>/"): return None if m.group(1).startswith("./"): return None return MissingNodeModule(m.group(1)) @problem("command-missing") class MissingCommand: command: str def __str__(self): return "Missing command: %s" % self.command @problem("no-secret-gpg-key") class MissingSecretGpgKey: def __str__(self): return "No secret GPG key is present" @problem("no-vcversioner-version") class MissingVcVersionerVersion: def __str__(self): return "vcversion could not find a git directory or version.txt file" @problem("missing-configure") class MissingConfigure: def __str__(self): return "Missing configure script" def command_missing(m): command = m.group(1) if "PKGBUILDDIR" in command: return None if command == "./configure": return MissingConfigure() if command.startswith("./") or command.startswith("../"): return None if command == "debian/rules": return None return MissingCommand(command) @problem("javascript-runtime-missing") class MissingJavaScriptRuntime: def __str__(self): return "Missing JavaScript Runtime" @problem("missing-php-extension") class MissingPHPExtension: extension: str def __str__(self): return "Missing PHP Extension: %s" % self.extension @problem("minimum-autoconf-too-old") class MinimumAutoconfTooOld: minimum_version: str def __str__(self): return "configure.{ac,in} should require newer autoconf %s" % self.minimum_version @problem("missing-pkg-config-package") class MissingPkgConfig: module: str minimum_version: Optional[str] = None def __str__(self): if self.minimum_version: return "Missing pkg-config file: %s (>= %s)" % ( self.module, self.minimum_version, ) else: return "Missing pkg-config file: %s" % self.module def __repr__(self): return "%s(%r, minimum_version=%r)" % ( type(self).__name__, self.module, self.minimum_version, ) @problem("missing-go-runtime") class MissingGoRuntime: def __str__(self): return "go runtime is missing" def pkg_config_missing(m): expr = m.group(1).strip().split("\t")[0] if ">=" in expr: pkg, minimum = expr.split(">=", 1) return MissingPkgConfig(pkg.strip(), minimum.strip()) if " " not in expr: return MissingPkgConfig(expr) # Hmm return None @problem("missing-boost-components") class MissingBoostComponents: components: List[str] def __str__(self): return "Missing Boost components: %r" % self.components @problem("missing-cmake-files") class CMakeFilesMissing: filenames: List[str] version: Optional[str] = None def __str__(self): if self.version: return "Missing CMake package configuration files (version %s): %r" % (self.version, self.filenames,) return "Missing CMake package configuration files: %r" % (self.filenames,) @problem("missing-cmake-config") class MissingCMakeConfig: name: str version: str def __str__(self): if self.version: return "Missing CMake package configuration for %s (version %s)" % (self.name, self.version) return "Missing CMake package configuration for %s" % (self.name, ) @problem("debhelper-argument-order") class DhWithOrderIncorrect: def __str__(self): return "dh argument order is incorrect" @problem("unsupported-debhelper-compat-level") class UnsupportedDebhelperCompatLevel: oldest_supported: int requested: int def __str__(self): return "Request debhelper compat level %d lower than supported %d" % ( self.requested, self.oldest_supported) @problem("no-space-on-device", is_global=True) class NoSpaceOnDevice: def __str__(self): return "No space on device" @problem("missing-perl-predeclared") class MissingPerlPredeclared: name: str def __str__(self): return "missing predeclared function: %s" % self.name @problem("missing-perl-distribution-file") class MissingPerlDistributionFile: filename: str def __str__(self): return "Missing perl distribution file: %s" % self.filename @problem("missing-perl-module") class MissingPerlModule: filename: Optional[str] module: str inc: Optional[List[str]] = None minimum_version: Optional[str] = None def __str__(self): if self.filename: return "Missing Perl module: %s (filename: %r)" % ( self.module, self.filename, ) else: return "Missing Perl Module: %s" % self.module @problem("missing-perl-file") class MissingPerlFile: filename: str inc: Optional[List[str]] = None def __str__(self): return "Missing Perl file: %s (inc: %r)" % (self.filename, self.inc) class MissingMavenArtifacts(Problem): kind = "missing-maven-artifacts" def __init__(self, artifacts): self.artifacts = artifacts def __eq__(self, other): return isinstance(other, type(self)) and self.artifacts == other.artifacts def __str__(self): return "Missing maven artifacts: %r" % self.artifacts def __repr__(self): return "%s(%r)" % (type(self).__name__, self.artifacts) @problem("dh-until-unsupported") class DhUntilUnsupported: def __str__(self): return "dh --until is no longer supported" @problem("dh-addon-load-failure") class DhAddonLoadFailure: name: str path: str def __str__(self): return "dh addon loading failed: %s" % self.name @problem("dh-missing-uninstalled") class DhMissingUninstalled: missing_file: str def __str__(self): return "File built by Debian not installed: %r" % self.missing_file @problem("dh-link-destination-is-directory") class DhLinkDestinationIsDirectory: path: str def __str__(self): return "Link destination %s is directory" % self.path def maven_missing_artifact(m): artifacts = m.group(1).split(",") return MissingMavenArtifacts([a.strip() for a in artifacts]) @problem("missing-xml-entity") class MissingXmlEntity: url: str def __str__(self): return "Missing XML entity: %s" % self.url @problem("ccache-error") class CcacheError: error: str def __str__(self): return "ccache error: %s" % self.error @problem("missing-library") class MissingLibrary: library: str def __str__(self): return "missing library: %s" % self.library @problem("missing-static-library") class MissingStaticLibrary: library: str filename: str def __str__(self): return "missing static library: %s" % self.library @problem("missing-ruby-gem") class MissingRubyGem: gem: str version: Optional[str] = None def __str__(self): if self.version: return "missing ruby gem: %s (>= %s)" % (self.gem, self.version) else: return "missing ruby gem: %s" % self.gem def ruby_missing_gem(m): minimum_version = None for grp in m.group(2).split(","): (cond, val) = grp.strip().split(" ", 1) if cond == ">=": minimum_version = val break if cond == "~>": minimum_version = val return MissingRubyGem(m.group(1), minimum_version) @problem("missing-ruby-file") class MissingRubyFile: filename: str def __str__(self): return "Missing ruby file: %s" % (self.filename,) @problem("missing-php-class") class MissingPhpClass: php_class: str def __str__(self): return "missing PHP class: %s" % self.php_class @problem("missing-java-class") class MissingJavaClass: classname: str def __str__(self): return "missing java class: %s" % self.classname @problem("missing-r-package") class MissingRPackage: package: str minimum_version: Optional[str] = None def __str__(self): if self.minimum_version: return "missing R package: %s (>= %s)" % ( self.package, self.minimum_version, ) else: return "missing R package: %s" % self.package def r_missing_package(m): fragment = m.group(1) deps = [dep.strip("‘’' ") for dep in fragment.split(",")] return MissingRPackage(deps[0]) @problem("debhelper-pattern-not-found") class DebhelperPatternNotFound: pattern: str tool: str directories: List[str] def __str__(self): return "debhelper (%s) expansion failed for %r (directories: %r)" % ( self.tool, self.pattern, self.directories, ) @problem("missing-gnome-common") class GnomeCommonMissing: def __str__(self): return "gnome-common is not installed" @problem("missing-xfce-dependency") class MissingXfceDependency: package: str def __str__(self): return "Missing XFCE build dependency: %s" % (self.package) @problem("missing-automake-input") class MissingAutomakeInput: path: str def __str__(self): return "automake input file %s missing" % self.path @problem("missing-autoconf-macro") class MissingAutoconfMacro: macro: str need_rebuild: bool = False def __str__(self): return "autoconf macro %s missing" % self.macro @problem("missing-gnome-common-dependency") class MissingGnomeCommonDependency: package: str minimum_version: Optional[str] = None def __str__(self): return "Missing gnome-common dependency: %s: (>= %s)" % ( self.package, self.minimum_version, ) @problem("missing-config.status-input") class MissingConfigStatusInput: path: str def __str__(self): return "missing config.status input %s" % self.path @problem("missing-jvm") class MissingJVM: def __str__(self): return "Missing JVM" @problem("missing-perl-manifest") class MissingPerlManifest: def __str__(self): return "missing Perl MANIFEST" @problem("upstart-file-present") class UpstartFilePresent: filename: str def __str__(self): return "Upstart file present: %s" % self.filename @problem("need-pg-buildext-updatecontrol") class NeedPgBuildExtUpdateControl: generated_path: str template_path: str def __str__(self): return "Need to run 'pg_buildext updatecontrol' to update %s" % ( self.generated_path ) @problem("missing-vala-package") class MissingValaPackage: package: str def __str__(self): return "Missing Vala package: %s" % self.package MAVEN_ERROR_PREFIX = "(?:\\[ERROR\\]|\\[\x1b\\[1;31mERROR\x1b\\[m\\]) " @problem("local-directory-not-existing") class DirectoryNonExistant: path: str def __str__(self): return "Directory does not exist: %s" % self.path @problem("imagemagick-delegate-missing") class ImageMagickDelegateMissing: delegate: str def __str__(self): return "Imagemagick missing delegate: %s" % self.delegate @problem("debian-version-rejected") class DebianVersionRejected: version: str def __str__(self): return "Debian Version Rejected; %s" % self.version class MissingHaskellDependencies(Problem): kind = "missing-haskell-dependencies" def __init__(self, deps): self.deps = deps def __eq__(self, other): return isinstance(other, type(self)) and self.deps == other.deps def __repr__(self): return "%s(%r)" % (type(self).__name__, self.deps) def __str__(self): return "Missing Haskell dependencies: %r" % self.deps class MissingHaskellModule(Problem): kind = "missing-haskell-module" def __init__(self, module): self.module = module def __eq__(self, other): return isinstance(other, type(self)) and self.mdule == other.module def __repr__(self): return "%s(%r)" % (type(self).__name__, self.module) def __str__(self): return "Missing Haskell module: %r" % self.module class Matcher(object): def match(self, line: List[str], i: int) -> Tuple[List[int], Optional[Problem]]: raise NotImplementedError(self.match) class SingleLineMatcher(Matcher): def __init__(self, regexp, cb=None): self.regexp = re.compile(regexp) self.cb = cb def match(self, lines, i): m = self.regexp.match(lines[i].rstrip("\n")) if not m: return [], None if self.cb: err = self.cb(m) else: err = None return [i], err @problem("missing-setup.py-command") class MissingSetupPyCommand: command: str def __str__(self): return "missing setup.py subcommand: %s" % self.command class SetupPyCommandMissingMatcher(Matcher): final_line_re = re.compile(r"error: invalid command \'(.*)\'") warning_match = re.compile( r"usage: setup.py \[global_opts\] cmd1 " r"\[cmd1_opts\] \[cmd2 \[cmd2_opts\] \.\.\.\]" ) def match(self, lines, i): m = self.final_line_re.fullmatch(lines[i].rstrip("\n")) if not m: return [], None for j in range(i, max(0, i - 20), -1): if self.warning_match.fullmatch(lines[j].rstrip("\n")): return [i], MissingSetupPyCommand(m.group(1)) return [], None class MultiLinePerlMissingModulesError(Matcher): def match(self, lines, i): if lines[i].rstrip("\n") != "# The following modules are not available.": return [], None if lines[i+1].rstrip("\n") != "# `perl Makefile.PL | cpanm` will install them:": return [], None relevant_linenos = [i, i + 1, i + 2] return relevant_linenos, MissingPerlModule(lines[i+2].strip()) class MultiLineConfigureError(Matcher): submatchers = [ ( re.compile(r"\s*Unable to find (.*) \(http(.*)\)"), lambda m: MissingVagueDependency(m.group(1), url=m.group(2)), ), ( re.compile(r"\s*Unable to find (.*)\."), lambda m: MissingVagueDependency(m.group(1)), ), ] def match(self, lines, i): if lines[i].rstrip("\n") != "configure: error:": return [], None relevant_linenos = [] for j, line in enumerate(lines[i + 1 :], i + 1): if not line.strip(): continue relevant_linenos.append(j) for submatcher, fn in self.submatchers: m = submatcher.match(line.rstrip("\n")) if m: return [j], fn(m) return relevant_linenos, None class AutoconfUnexpectedMacroMatcher(Matcher): regexp1 = re.compile( r".*\.\/configure: line [0-9]+: syntax error near unexpected token `.+\'" ) regexp2 = re.compile(r".*\.\/configure: line [0-9]+: `\s*([A-Z0-9_]+)\(.*") def match(self, lines, i): m = self.regexp1.fullmatch(lines[i].rstrip("\n")) if not m: return [], None try: m = self.regexp2.fullmatch(lines[i + 1].rstrip("\n")) except IndexError: return [], None if m: return [i, i + 1], MissingAutoconfMacro(m.group(1), need_rebuild=True) return [], None class PythonFileNotFoundErrorMatcher(Matcher): final_line_re = re.compile( r"^(?:E +)?FileNotFoundError: \[Errno 2\] " r"No such file or directory: \'(.*)\'" ) def match(self, lines, i): m = self.final_line_re.fullmatch(lines[i].rstrip("\n")) if not m: return [], None if i - 2 >= 0 and "subprocess" in lines[i - 2]: return [i], MissingCommand(m.group(1)) return [i], file_not_found_maybe_executable(m) class HaskellMissingDependencyMatcher(Matcher): regexp = re.compile(r"(.*): Encountered missing or private dependencies:") def match(self, lines, i): m = self.regexp.fullmatch(lines[i].rstrip("\n")) if not m: return [], None deps = [] linenos = [i] for line in lines[i + 1 :]: if not line.strip("\n"): break deps.extend([x.strip() for x in line.split(",", 1)]) linenos.append(linenos[-1] + 1) return linenos, MissingHaskellDependencies([dep for dep in deps if dep]) def cmake_compiler_failure(m): compiler_output = textwrap.dedent(m.group(3)) match, error = find_build_failure_description(compiler_output.splitlines(True)) return error def cmake_compiler_missing(m): if m.group(1) == "Fortran": return MissingFortranCompiler() return None class CMakeNeedExactVersion(Problem): kind = "cmake-exact-version-missing" def __init__(self, package, version_found, exact_version_needed, path): self.package = package self.version_found = version_found self.exact_version_needed = exact_version_needed self.path = path def __eq__(self, other): return isinstance(other, type(self)) and ( self.package == other.package and self.version_found == other.version_found and self.exact_version_needed == other.exact_version_needed and self.path == other.path ) def __repr__(self): return "%s(%r, %r, %r, %r)" % ( type(self).__name__, self.package, self.version_found, self.exact_version_needed, self.path, ) def __str__(self): return "CMake needs exact package %s, version %s" % ( self.package, self.exact_version_needed, ) class CMakeErrorMatcher(Matcher): regexp = re.compile(r"CMake (Error|Warning) at (.+):([0-9]+) \((.*)\):") cmake_errors = [ (r'Could NOT find Boost \(missing: (.*)\) \(found suitable version .*', lambda m: MissingBoostComponents(m.group(1).split())), ( r"-- Package \'(.*)\', required by \'(.*)\', not found", lambda m: MissingPkgConfig(m.group(1)), ), ( r'Could not find a package configuration file provided by\s' r'"(.*)" \(requested\sversion\s(.*)\)\swith\sany\s+of\s+the\s+following\snames:' r'\n\n( .*\n)+\n.*$', lambda m: CMakeFilesMissing( [e.strip() for e in m.group(3).splitlines()], m.group(2)) ), ( r"Could NOT find (.*) \(missing: .*\)", lambda m: MissingVagueDependency(m.group(1)), ), ( r'The (.+) compiler\n\n "(.*)"\n\nis not able to compile a ' r"simple test program\.\n\nIt fails with the following output:\n\n" r"(.*)\n\n" r"CMake will not be able to correctly generate this project.\n$", cmake_compiler_failure, ), ( r"Could NOT find (.*): Found unsuitable version \"(.*)\",\sbut\s" r"required\sis\sexact version \"(.*)\" \(found\s(.*)\)", lambda m: CMakeNeedExactVersion( m.group(1), m.group(2), m.group(3), m.group(4) ), ), ( r"(.*) couldn't be found \(missing: .*_LIBRARIES .*_INCLUDE_DIR\)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"Could NOT find (.*): Found unsuitable version \"(.*)\",\sbut\s" r"required\sis\sat\sleast\s\"(.*)\" \(found\s(.*)\)", lambda m: MissingPkgConfig(m.group(1), m.group(3)), ), ( r'The imported target \"(.*)\" references the file\n\n\s*"(.*)"\n\n' r"but this file does not exist\.(.*)", lambda m: MissingFile(m.group(2)), ), ( r'Could not find a configuration file for package "(.*)"\sthat\sis\s' r'compatible\swith\srequested\sversion\s"(.*)"\.', lambda m: MissingCMakeConfig(m.group(1), m.group(2)), ), ( r'.*Could not find a package configuration file provided by "(.*)"\s+' r"with\s+any\s+of\s+the\s+following\s+names:\n\n( .*\n)+\n.*$", lambda m: CMakeFilesMissing([e.strip() for e in m.group(2).splitlines()]) ), ( r'.*Could not find a package configuration file provided by "(.*)"\s' r"\(requested\sversion\s(.+\))\swith\sany\sof\sthe\sfollowing\snames:\n" r"\n( .*\n)+\n.*$", lambda m: CMakeFilesMissing([e.strip() for e in m.group(3).splitlines()], m.group(2)), ), ( r"No CMAKE_(.*)_COMPILER could be found.\n" r"\n" r"Tell CMake where to find the compiler by setting either" r'\sthe\senvironment\svariable\s"(.*)"\sor\sthe\sCMake\scache' r"\sentry\sCMAKE_(.*)_COMPILER\sto\sthe\sfull\spath\sto" r"\sthe\scompiler,\sor\sto\sthe\scompiler\sname\sif\sit\sis\sin\s" r"the\sPATH.\n", lambda m: MissingCommand(m.group(1).lower()), ), (r'file INSTALL cannot find\s"(.*)".\n', lambda m: MissingFile(m.group(1))), ( r'file INSTALL cannot copy file\n"(.*)"\sto\s"(.*)":\s' r"No space left on device.\n", lambda m: NoSpaceOnDevice(), ), ( r"patch: \*\*\*\* write error : No space left on device", lambda m: NoSpaceOnDevice(), ), ( r".*\(No space left on device\)", lambda m: NoSpaceOnDevice(), ), (r'file INSTALL cannot copy file\n"(.*)"\nto\n"(.*)"\.\n', None), (r"Could NOT find (.*) \(missing: (.*)\)", None), ( r"Missing (.*)\. Either your\n" r"lib(.*) version is too old, or lib(.*) wasn\'t found in the place you\n" r"said.", lambda m: MissingLibrary(m.group(1)), ), ( r"need (.*) of version (.*)", lambda m: MissingVagueDependency( m.group(1), minimum_version=m.group(2).strip() ), ), ( r"\*\*\* (.*) is required to build (.*)\n", lambda m: MissingVagueDependency(m.group(1)), ), (r"\[([^ ]+)\] not found", lambda m: MissingVagueDependency(m.group(1))), (r"([^ ]+) not found", lambda m: MissingVagueDependency(m.group(1))), (r"error: could not find git .*", lambda m: MissingCommand("git")), (r'Could not find \'(.*)\' executable[\!,].*', lambda m: MissingCommand(m.group(1))), (r'Could not find (.*)_STATIC_LIBRARIES using the following names: ([a-zA-z0-9_.]+)', lambda m: MissingStaticLibrary(m.group(1), m.group(2))), ('include could not find (requested|load) file:\n\n (.*)\n', lambda m: CMakeFilesMissing([m.group(2) + '.cmake' if not m.group(2).endswith('.cmake') else m.group(2)])), (r'(.*) and (.*) are required', lambda m: MissingVagueDependency(m.group(1))), (r'Please check your (.*) installation', lambda m: MissingVagueDependency(m.group(1))), (r'Python module (.*) not found\!', lambda m: MissingPythonModule(m.group(1))), (r'\s*could not find ([^\s]+)$', lambda m: MissingVagueDependency(m.group(1))), (r'Please install (.*) before installing (.*)\.', lambda m: MissingVagueDependency(m.group(1))), (r"Please get (.*) from (www\..*)", lambda m: MissingVagueDependency(m.group(1), url=m.group(2))), (r'Found unsuitable Qt version "" from NOTFOUND, ' r'this code requires Qt 4.x', lambda m: MissingQt('4')), (r'(.*) executable not found\! Please install (.*)\.', lambda m: MissingCommand(m.group(2))), (r'Could not find the OpenGL external dependency\.', lambda m: MissingLibrary('GL')), (r'(.*) tool not found', lambda m: MissingCommand(m.group(1))), (r'-- Requested \'(.*) >= (.*)\' but version of (.*) is (.*)', lambda m: MissingPkgConfig(m.group(1), m.group(2))), (r'-- No package \'(.*)\' found', lambda m: MissingPkgConfig(m.group(1))), (r'([^ ]+) library not found\.', lambda m: MissingLibrary(m.group(1))), (r'Please install (.*) so that it is on the PATH and try again\.', command_missing), (r'-- Unable to find git\. Setting git revision to \'unknown\'\.', lambda m: MissingCommand('git')), (r'(.*) must be installed before configuration \& building can ' r'proceed', lambda m: MissingVagueDependency(m.group(1))), (r'(.*) development files not found\.', lambda m: MissingVagueDependency(m.group(1))), (r'.* but no (.*) dev libraries found', lambda m: MissingVagueDependency(m.group(1))), (r'Failed to find (.*) \(missing: .*\)', lambda m: MissingVagueDependency(m.group(1))), (r'Couldn\'t find ([^ ]+) development files\..*', lambda m: MissingVagueDependency(m.group(1))), (r'Could not find required (.*) package\!', lambda m: MissingVagueDependency(m.group(1))), (r'Cannot find (.*), giving up\. .*', lambda m: MissingVagueDependency(m.group(1))), (r'Cannot find (.*)\. (.*) is required for (.*)', lambda m: MissingVagueDependency(m.group(1))), (r'The development\sfiles\sfor\s(.*)\sare\s' r'required\sto\sbuild (.*)\.', lambda m: MissingVagueDependency(m.group(1))), (r'Required library (.*) not found\.', lambda m: MissingVagueDependency(m.group(1))), (r'(.*) required to compile (.*)', lambda m: MissingVagueDependency(m.group(1))), (r'(.*) requires (.*)', lambda m: MissingVagueDependency(m.group(2))), (r'Could not find ([A-Za-z-]+)', lambda m: MissingVagueDependency(m.group(1))), (r'(.+) is required for (.*)\.', lambda m: MissingVagueDependency(m.group(1))), (r'No (.+) version could be found in your system\.', lambda m: MissingVagueDependency(m.group(1))), (r'([^ ]+) >= (.*) is required', lambda m: MissingVagueDependency(m.group(1), m.group(2))), (r'([^ ]+) binary not found\!', lambda m: MissingCommand(m.group(1))), ] @classmethod def _extract_error_lines(cls, lines, i): linenos = [i] error_lines = [] for j, line in enumerate(lines[i + 1 :]): if line.rstrip('\n') and not line.startswith(" "): break error_lines.append(line.rstrip('\n') + '\n') linenos.append(i + 1 + j) while error_lines and error_lines[-1].rstrip('\n') == "": error_lines.pop(-1) linenos.pop(-1) return linenos, textwrap.dedent("".join(error_lines)).splitlines(True) def match(self, lines, i): m = self.regexp.fullmatch(lines[i].rstrip("\n")) if not m: return [], None path = m.group(2) # noqa: F841 start_lineno = int(m.group(3)) # noqa: F841 linenos, error_lines = self._extract_error_lines(lines, i) for r, fn in self.cmake_errors: m = re.match(r, "".join(error_lines), flags=re.DOTALL) if m: if fn is None: error = None else: error = fn(m) return linenos, error return linenos, None @problem("missing-fortran-compiler") class MissingFortranCompiler: def __str__(self): return "No Fortran compiler found" @problem("missing-c#-compiler") class MissingCSharpCompiler: def __str__(self): return "No C# compiler found" @problem("missing-rust-compiler") class MissingRustCompiler: def __str__(self): return "No Rust compiler found" @problem("missing-libtool") class MissingLibtool: def __str__(self): return "Libtool is missing" @problem("missing-pytest-fixture") class MissingPytestFixture: fixture: str def __str__(self): return "Missing pytest fixture: %s" % self.fixture @problem("missing-cargo-crate") class MissingCargoCrate: crate: str requirement: Optional[str] = None def __str__(self): if self.requirement: return "Missing crate: %s (%s)" % (self.crate, self.requirement) else: return "Missing crate: %s" % self.crate def cargo_missing_requirement(m): try: crate, requirement = m.group(1).split(" ", 1) except ValueError: crate = m.group(1) requirement = None return MissingCargoCrate(crate, requirement) @problem("missing-latex-file") class MissingLatexFile: filename: str def __str__(self): return "Missing LaTeX file: %s" % self.filename @problem("missing-fontspec") class MissingFontspec: fontspec: str def __str__(self): return "Missing font spec: %s" % self.fontspec @problem("missing-dh-compat-level") class MissingDHCompatLevel: command: str def __str__(self): return "Missing DH Compat Level (command: %s)" % self.command @problem("duplicate-dh-compat-level") class DuplicateDHCompatLevel: command: str def __str__(self): return "DH Compat Level specified twice (command: %s)" % self.command @problem("missing-introspection-typelib") class MissingIntrospectionTypelib: library: str def __str__(self): return "Missing introspection typelib: %s" % self.library @problem("unknown-certificate-authority") class UnknownCertificateAuthority: url: str def __str__(self): return "Unknown Certificate Authority for %s" % self.url @problem("missing-x-display") class MissingXDisplay: def __str__(self): return "No X Display" @problem("missing-postgresql-extension") class MissingPostgresExtension: extension: str def __str__(self): return "Missing postgres extension: %s" % self.extension @problem("missing-lua-module") class MissingLuaModule: module: str def __str__(self): return "Missing Lua Module: %s" % self.module @problem("cancelled") class Cancelled: def __str__(self): return "Cancelled by runner or job manager" @problem("inactive-killed") class InactiveKilled: minutes: int def __str__(self): return "Killed due to inactivity" @problem("missing-pause-credentials") class MissingPauseCredentials: def __str__(self): return "Missing credentials for PAUSE" @problem("mismatch-gettext-versions") class MismatchGettextVersions: makefile_version: str autoconf_version: str def __str__(self): return "Mismatch versions (%s, %s)" % ( self.makefile_version, self.autoconf_version) @problem("disappeared-symbols") class DisappearedSymbols: def __str__(self): return "Disappeared symbols" @problem("missing-gnulib-directory") class MissingGnulibDirectory: directory: str def __str__(self): return "Missing gnulib directory %s" % self.directory @problem("missing-go.mod-file") class MissingGoModFile: def __str__(self): return "go.mod file is missing" @problem("outdated-go.mod-file") class OutdatedGoModFile: def __str__(self): return "go.mod file is outdated" build_failure_regexps = [ ( r"make\[[0-9]+\]: \*\*\* No rule to make target " r"\'(.*)\', needed by \'.*\'\. Stop\.", file_not_found, ), ( r"make: \*\*\* No rule to make target " r"\'(\/.*)\'\. Stop\.", file_not_found, ), (r"[^:]+:\d+: (.*): No such file or directory", file_not_found_maybe_executable), ( r"(distutils.errors.DistutilsError|error): " r"Could not find suitable distribution " r"for Requirement.parse\(\'([^\']+)\'\)", lambda m: MissingPythonDistribution.from_requirement_str(m.group(2).split(";")[0]), ), ( r"We need the Python library (.*) to be installed. " r"Try runnning: python -m ensurepip", lambda m: MissingPythonDistribution(m.group(1)), ), ( r"pkg_resources.DistributionNotFound: The \'([^\']+)\' " r"distribution was not found and is required by the application", lambda m: MissingPythonDistribution.from_requirement_str(m.group(1)), ), ( r"pkg_resources.DistributionNotFound: The \'([^\']+)\' " r"distribution was not found and is required by (.*)", lambda m: MissingPythonDistribution.from_requirement_str(m.group(1)), ), ( r"Please install cmake version \>= (.*) and re-run setup", lambda m: MissingCommand("cmake"), ), ( r"pluggy.manager.PluginValidationError: " r"Plugin \'.*\' could not be loaded: " r"\(.* \(/usr/lib/python2.[0-9]/dist-packages\), " r"Requirement.parse\(\'(.*)\'\)\)\!", python2_reqs_not_found, ), ( r"ImportError: cannot import name (.*), introspection typelib not found", lambda m: MissingIntrospectionTypelib(m.group(1)), ), ( r"ValueError: Namespace (.*) not available", lambda m: MissingIntrospectionTypelib(m.group(1)), ), ( r' namespace \'(.*)\' ([^ ]+) is being loaded, but \>= ([^ ]+) is required', lambda m: MissingRPackage(m.group(1), minimum_version=m.group(3)) ), ( "ImportError: cannot import name '(.*)' from '(.*)'", lambda m: MissingPythonModule(m.group(2) + "." + m.group(1), python_version=None) ), ("E fixture '(.*)' not found", lambda m: MissingPytestFixture(m.group(1))), ( "E ImportError: cannot import name '(.*)' from '(.*)'", lambda m: MissingPythonModule(m.group(2) + "." + m.group(1), python_version=None) ), ("E ImportError: cannot import name ([^']+)", python_module_not_found), ( r"django.core.exceptions.ImproperlyConfigured: Error loading .* module: " r"No module named \'(.*)\'", python_module_not_found, ), ("E ImportError: No module named (.*)", python_module_not_found), ( r"\s*ModuleNotFoundError: No module named '(.*)'", lambda m: MissingPythonModule(m.group(1), python_version=3)), ( r"Could not import extension .* \(exception: No module named (.*)\)", lambda m: MissingPythonModule(m.group(1).strip("'")), ), ( r"^(.*): Error while finding module specification for " r"'(.*)' \(ModuleNotFoundError: No module named '(.*)'\)", python_cmd_module_not_found, ), ( "E ModuleNotFoundError: No module named '(.*)'", lambda m: MissingPythonModule(m.group(1), python_version=3) ), ( r"/usr/bin/python3: No module named (.*)", lambda m: MissingPythonModule(m.group(1), python_version=3) ), ('(.*:[0-9]+|package .*): cannot find package "(.*)" in any of:', lambda m: MissingGoPackage(m.group(2))), ( r'ImportError: Error importing plugin ".*": No module named (.*)', python_module_not_found, ), ("ImportError: No module named (.*)", python_module_not_found), ( r"[^:]+:\d+:\d+: fatal error: (.+\.h|.+\.hh|.+\.hpp): No such file or directory", lambda m: MissingCHeader(m.group(1)), ), ( r"[^:]+:\d+:\d+: fatal error: (.+\.xpm): No such file or directory", None, ), ( r'.*fatal: not a git repository \(or any parent up to mount point \/\)', lambda m: VcsControlDirectoryNeeded(['git']), ), ( r'.*fatal: not a git repository \(or any of the parent directories\): \.git', lambda m: VcsControlDirectoryNeeded(['.git']), ), ( r"[^:]+\.[ch]:\d+:\d+: fatal error: (.+): No such file or directory", lambda m: MissingCHeader(m.group(1)), ), ("✖ \x1b\\[31mERROR:\x1b\\[39m Cannot find module '(.*)'", node_module_missing), ("\x1b\\[2mError: Cannot find module '(.*)'", node_module_missing), ("\x1b\\[1m\x1b\\[31m\\[!\\] \x1b\\[1mError: Cannot find module '(.*)'", node_module_missing), ('\x1b\\[1m\x1b\\[31m\\[\\!\\] \x1b\\[1mError: Cannot find module \'(.*)\'', node_module_missing), ("✖ \x1b\\[31mERROR:\x1b\\[39m Error: Cannot find module '(.*)'", node_module_missing), ("\x1b\\[0;31m Error: To use the transpile option, you must have the '(.*)' module installed", node_module_missing), ('\\[31mError: No test files found: "(.*)"\\[39m', None), (r'\x1b\[31mError: No test files found: "(.*)"\x1b\[39m', None), (r"\s*Error: Cannot find module \'(.*)\'", node_module_missing), (r">> Error: Cannot find module \'(.*)\'", node_module_missing), (r'Error: Failed to load parser \'.*\' declared in \'.*\': ' r'Cannot find module \'(.*)\'', lambda m: MissingNodeModule(m.group(1))), (r' Cannot find module \'(.*)\' from \'.*\'', lambda m: MissingNodeModule(m.group(1))), (r'>> Error: Grunt attempted to load a \.coffee file ' r'but CoffeeScript was not installed\.', lambda m: MissingNodePackage('coffeescript')), ( r">> Got an unexpected exception from the coffee-script compiler. " r"The original exception was: Error: Cannot find module \'(.*)\'", node_module_missing, ), ( r"\s*Module not found: Error: Can\'t resolve \'(.*)\' in \'(.*)\'", node_module_missing, ), ( r" Module (.*) in the transform option was not found\.", node_module_missing, ), ( r"libtool/glibtool not found\!", lambda m: MissingVagueDependency("libtool"), ), (r"qmake: could not find a Qt installation of \'\'", lambda m: MissingQt()), (r"Cannot find X include files via .*", lambda m: MissingX11()), ( r"\*\*\* No X11\! Install X-Windows development headers/libraries\! \*\*\*", lambda m: MissingX11(), ), ( r"configure: error: \*\*\* No X11\! Install X-Windows development headers/libraries\! \*\*\*", lambda m: MissingX11(), ), ( r"configure: error: The Java compiler javac failed.*", lambda m: MissingCommand('javac') ), ( r"ERROR: InvocationError for command could not find executable (.*)", lambda m: MissingCommand(m.group(1)) ), ( r" \*\*\* The (.*) script could not be found\. .*", lambda m: MissingCommand(m.group(1)), ), ( r"(.*)\" command could not be found. (.*)", lambda m: MissingCommand(m.group(1)), ), (r'\>\> Local Npm module \"(.*)" not found. Is it installed?', node_module_missing), ( r"npm ERR\! CLI for webpack must be installed.", lambda m: MissingNodePackage("webpack-cli"), ), (r"npm ERR\! \[\!\] Error: Cannot find module '(.*)'", node_module_missing), ( r'npm ERR\! \>\> Local Npm module "(.*)" not found. Is it installed\?', node_module_missing, ), (r"npm ERR\! Error: Cannot find module '(.*)'", node_module_missing), ( r"npm ERR\! ERROR in Entry module not found: " r"Error: Can't resolve '(.*)' in '.*'", node_module_missing, ), (r'npm ERR\! sh: [0-9]+: (.*): not found', command_missing), (r'npm ERR\! (.*\.ts)\([0-9]+,[0-9]+\): error TS[0-9]+: Cannot find module \'(.*)\' or its corresponding type declarations.', lambda m: MissingNodeModule(m.group(2))), ( r"(\.\/configure): line \d+: ([A-Z0-9_]+): command not found", lambda m: MissingAutoconfMacro(m.group(2)), ), (r".*: line \d+: ([^ ]+): command not found", command_missing), (r".*: line \d+: ([^ ]+): Permission denied", None), (r"make\[[0-9]+\]: .*: Permission denied", None), (r'/usr/bin/texi2dvi: TeX neither supports -recorder nor outputs \\openout lines in its log file', None), (r"\/bin\/sh: \d+: ([^ ]+): not found", command_missing), (r"sh: \d+: ([^ ]+): not found", command_missing), (r".*\.sh: \d+: ([^ ]+): not found", command_missing), (r".*: 1: cd: can\'t cd to (.*)", lambda m: DirectoryNonExistant(m.group(1))), (r"\/bin\/bash: (.*): command not found", command_missing), (r"bash: ([^ ]+): command not found", command_missing), (r"env: ‘(.*)’: No such file or directory", interpreter_missing), ( r"\/bin\/bash: .*: (.*): bad interpreter: No such file or directory", interpreter_missing, ), # SH error (r".*: [0-9]+: exec: (.*): not found", command_missing), (r".*: [0-9]+: (.*): not found", command_missing), (r"/usr/bin/env: ‘(.*)’: No such file or directory", command_missing), (r"/usr/bin/env: \'(.*)\': No such file or directory", command_missing), (r"make\[[0-9]+\]: (.*): Command not found", command_missing), (r"make: (.*): Command not found", command_missing), (r"make: (.*): No such file or directory", command_missing), (r"xargs: (.*): No such file or directory", command_missing), (r"make\[[0-9]+\]: ([^/ :]+): No such file or directory", command_missing), (r".*: failed to exec \'(.*)\': No such file or directory", command_missing), (r"No package \'([^\']+)\' found", pkg_config_missing), ( r"\-\- Please install Git, make sure it is in your path, and then try again.", lambda m: MissingCommand("git"), ), ( r'\+ERROR: could not access file "(.*)": No such file or directory', lambda m: MissingPostgresExtension(m.group(1)), ), ( r"configure: error: (Can't|Cannot) find \"(.*)\" in your PATH.*", lambda m: MissingCommand(m.group(2)), ), ( r"configure: error: Cannot find (.*) in your system path", lambda m: MissingCommand(m.group(1)), ), ( r'\> Cannot run program "(.*)": error=2, No such file or directory', lambda m: MissingCommand(m.group(1)), ), (r'(.*) binary \'(.*)\' not available .*', lambda m: MissingCommand(m.group(2))), (r'An error has occurred: FatalError: git failed\. ' r'Is it installed, and are you in a Git repository directory\?', lambda m: MissingCommand("git")), ("Please install '(.*)' seperately and try again.", lambda m: MissingCommand(m.group(1))), ( r"\> A problem occurred starting process \'command \'(.*)\'\'", lambda m: MissingCommand(m.group(1)), ), ( r"vcver.scm.git.GitCommandError: \'git .*\' returned an error code 127", lambda m: MissingCommand("git"), ), MultiLineConfigureError(), MultiLinePerlMissingModulesError(), (r"configure: error: No package \'([^\']+)\' found", pkg_config_missing), ( r"configure: error: (doxygen|asciidoc) is not available " r"and maintainer mode is enabled", lambda m: MissingCommand(m.group(1)), ), ( r"configure: error: Documentation enabled but rst2html not found.", lambda m: MissingCommand("rst2html"), ), ( r"cannot run pkg-config to check .* version at (.*) line [0-9]+\.", lambda m: MissingCommand("pkg-config"), ), (r"Error: pkg-config not found\!", lambda m: MissingCommand("pkg-config")), (r"\*\*\* pkg-config (.*) or newer\. You can download pkg-config", lambda m: MissingVagueDependency('pkg-config', minimum_version=m.group(1))), # Tox (r"ERROR: InterpreterNotFound: (.*)", lambda m: MissingCommand(m.group(1))), (r"ERROR: unable to find python", lambda m: MissingCommand("python")), (r" ERROR: BLAS not found\!", lambda m: MissingLibrary("blas")), AutoconfUnexpectedMacroMatcher(), (r"\./configure: [0-9]+: \.: Illegal option .*", None), (r"Requested \'(.*)\' but version of ([^ ]+) is ([^ ]+)", pkg_config_missing), ( r"configure: error: Package requirements \((.*)\) were not met:", pkg_config_missing, ), ( r"configure: error: [a-z0-9_-]+-pkg-config (.*) couldn\'t be found", pkg_config_missing, ), (r'configure: error: C preprocessor "/lib/cpp" fails sanity check', None), ( r"configure: error: .*\. Please install (bison|flex)", lambda m: MissingCommand(m.group(1)), ), ( r"configure: error: No C\# compiler found. You need to install either " r"mono \(>=(.*)\) or \.Net", lambda m: MissingCSharpCompiler(), ), ( r'configure: error: No C\# compiler found', lambda m: MissingCSharpCompiler(), ), ( r'error: can\'t find Rust compiler', lambda m: MissingRustCompiler(), ), ( r"configure: error: (.*) requires libkqueue \(or system kqueue\). .*", lambda m: MissingPkgConfig("libkqueue"), ), ( r"Did not find pkg-config by name \'pkg-config\'", lambda m: MissingCommand("pkg-config"), ), ( r'configure: error: Required (.*) binary is missing. Please install (.*).', lambda m: MissingCommand(m.group(1)) ), ( '.*meson.build:([0-9]+):([0-9]+): ERROR: Dependency "(.*)" not found', lambda m: MissingPkgConfig(m.group(3)) ), ( r".*meson.build:([0-9]+):([0-9]+): Unknown compiler\(s\): \[\['(.*)'.*\]", lambda m: MissingCommand(m.group(3)) ), ( '.*meson.build:([0-9]+):([0-9]+): ERROR: python3 "(.*)" missing', lambda m: MissingPythonModule(m.group(3), python_version=3), ), ( '.*meson.build:([0-9]+):([0-9]+): ERROR: Program \'(.*)\' not found', lambda m: MissingCommand(m.group(3)) ), ( '.*meson.build:([0-9]+):([0-9]+): ERROR: Git program not found, .*', lambda m: MissingCommand('git') ), ( '.*meson.build:([0-9]+):([0-9]+): ERROR: C header \'(.*)\' not found', lambda m: MissingCHeader(m.group(3)) ), ( r'configure: error: (.+\.h) could not be found\. Please set CPPFLAGS\.', lambda m: MissingCHeader(m.group(1)) ), ( r'.*meson.build:([0-9]+):([0-9]+): ERROR: Unknown compiler\(s\): \[\'(.*)\'\]', lambda m: MissingCommand(m.group(3)) ), ( '.*meson.build:([0-9]+):([0-9]+): ERROR: Dependency "(.*)" not found, ' "tried pkgconfig", lambda m: MissingPkgConfig(m.group(3)) ), ( r'.*meson.build:([0-9]+):([0-9]+): ERROR: Could not execute Vala compiler "(.*)"', lambda m: MissingCommand(m.group(3)) ), ( r'.*meson.build:([0-9]+):([0-9]+): ERROR: python3 is missing modules: (.*)', lambda m: MissingPythonModule(m.group(1)) ), ( r".*meson.build:([0-9]+):([0-9]+): ERROR: Invalid version of dependency, " r"need '([^']+)' \['>=\s*([^']+)'\] found '([^']+)'\.", lambda m: MissingPkgConfig(m.group(3), m.group(4)), ), ( ".*meson.build:([0-9]+):([0-9]+): ERROR: C shared or static library '(.*)' not found", lambda m: MissingLibrary(m.group(3)), ), ( ".*meson.build:([0-9]+):([0-9]+): ERROR: Pkg-config binary for machine .* not found. Giving up.", lambda m: MissingCommand("pkg-config"), ), ( ".*meson.build([0-9]+):([0-9]+): ERROR: Problem encountered: (.*) require (.*) >= (.*), (.*) which were not found.", lambda m: MissingVagueDependency(m.group(4), minimum_version=m.group(5)), ), ( r"ERROR: (.*) is not installed\. Install at least (.*) version (.+) to continue\.", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(3)) ), ( r"configure: error: Library requirements \((.*)\) not met\.", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: (.*) is missing -- (.*)", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: Cannot find (.*), check (.*)", lambda m: MissingVagueDependency(m.group(1), url=m.group(2)) ), ( r"configure: error: \*\*\* Unable to find (.* library)", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: unable to find (.*)\.", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: Perl Module (.*) not available", lambda m: MissingPerlModule(None, m.group(1)) ), ( r"(.*) was not found in your path\. Please install (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: Please install (.*) >= (.*)", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2)), ), ( r"configure: error: the required package (.*) is not installed", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: \*\*\* (.*) >= (.*) not installed.*", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2)) ), ( r"configure: error: you should install (.*) first", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: cannot locate (.*) >= (.*)", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2)) ), ( r"configure: error: \!\!\! Please install (.*) \!\!\!", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) version (.*) or higher is required", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2)), ), (r'configure: error: ([^ ]+) ([^ ]+) or better is required.*', lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2))), (r'configure: error: ([^ ]+) ([^ ]+) or greater is required.*', lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2))), (r'configure: error: ([^ ]+) or greater is required.*', lambda m: MissingVagueDependency(m.group(1))), ( r"configure: error: (.*) library is required", lambda m: MissingLibrary(m.group(1)), ), ( r'configure: error: OpenSSL developer library \'libssl-dev\' or ' r'\'openssl-devel\' not installed; cannot continue.', lambda m: MissingLibrary('ssl') ), ( r"configure: error: \*\*\* Cannot find (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) is required to compile .*", lambda m: MissingVagueDependency(m.group(1)) ), ( r'\s*You must have (.*) installed to compile .*\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r'You must install (.*) to compile (.*)', lambda m: MissingVagueDependency(m.group(1)) ), ( r'\*\*\* No (.*) found, please in(s?)tall it \*\*\*', lambda m: MissingVagueDependency(m.group(1)) ), ( r'\*\* ERROR \*\* : You must have `(.*)\' installed on your system\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r'autogen\.sh: ERROR: You must have `(.*)\' installed to compile ' r'this package\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r'autogen\.sh: You must have (.*) installed\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r'\s*Error\! You need to have (.*) installed\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r"(configure: error|\*\*Error\*\*): You must have (.*) installed.*", lambda m: MissingVagueDependency(m.group(2)), ), ( r"configure: error: (.*) is required for building this package.", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) is required to build (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) is required", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) is required for (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: \*\*\* (.*) is required\.", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: (.*) is required, please get it from (.*)", lambda m: MissingVagueDependency(m.group(1), url=m.group(2)) ), ( r"configure: error: .*, (lib[^ ]+) is required", lambda m: MissingVagueDependency(m.group(1)), ), ( r"dh: Unknown sequence --(.*) " r"\(options should not come before the sequence\)", lambda m: DhWithOrderIncorrect(), ), ( r"dh: Compatibility levels before ([0-9]+) are no longer supported " r"\(level ([0-9]+) requested\)", lambda m: UnsupportedDebhelperCompatLevel(int(m.group(1)), int(m.group(2))), ), (r'\{standard input\}: Error: (.*)', None), (r"dh: Unknown sequence (.*) \(choose from: .*\)", None), (r".*: .*: No space left on device", lambda m: NoSpaceOnDevice()), (r"^No space left on device.", lambda m: NoSpaceOnDevice()), ( r".*Can\'t locate (.*).pm in @INC \(you may need to install the " r"(.*) module\) \(@INC contains: (.*)\) at .* line [0-9]+\.", lambda m: MissingPerlModule(m.group(1) + ".pm", m.group(2), m.group(3).split(" ")), ), ( r".*Can\'t locate (.*).pm in @INC \(you may need to install the " r"(.*) module\) \(@INC contains: (.*)\)\.", lambda m: MissingPerlModule(m.group(1) + ".pm", m.group(2), m.group(3).split(" ")), ), ( r"\[DynamicPrereqs\] Can't locate (.*) at inline delegation in .*", lambda m: MissingPerlModule(None, m.group(1), None) ), ( r"Can't locate object method \"(.*)\" via package \"(.*)\" " r"\(perhaps you forgot to load \"(.*)\"\?\) at .*.pm line [0-9]+\.", lambda m: MissingPerlModule(None, m.group(2), None) ), ( r">\(error\): Could not expand \[(.*)\'", lambda m: MissingPerlModule(None, m.group(1).strip().strip("'"), None) ), ( r"\[DZ\] could not load class (.*) for license (.*)", lambda m: MissingPerlModule(None, m.group(1), None), ), ( r'\- ([^\s]+)\s+\.\.\.missing. \(would need (.*)\)', lambda m: MissingPerlModule( None, m.group(1), None, minimum_version=m.group(2)) ), ( r"Required plugin bundle ([^ ]+) isn\'t installed.", lambda m: MissingPerlModule(None, m.group(1), None) ), ( r"Required plugin ([^ ]+) isn\'t installed.", lambda m: MissingPerlModule(None, m.group(1), None) ), ( r".*Can\'t locate (.*) in @INC \(@INC contains: (.*)\) at .* line .*.", lambda m: MissingPerlFile(m.group(1), m.group(2).split(" ")), ), ( r"Can\'t find author dependency ([^ ]+) at (.*) line ([0-9]+).", lambda m: MissingPerlModule(None, m.group(1), None) ), ( r"Can\'t find author dependency ([^ ]+) version (.*) at (.*) line ([0-9]+).", lambda m: MissingPerlModule(None, m.group(1), minimum_version=m.group(2)) ), ( r"> Could not find (.*)\. Please check that (.*) contains a valid JDK " r"installation.", lambda m: MissingJDKFile(m.group(2), m.group(1)), ), ( r"> Could not find (.*)\. Please check that (.*) contains a valid " r"\(and compatible\) JDK installation.", lambda m: MissingJDKFile(m.group(2), m.group(1)), ), ( r"> Kotlin could not find the required JDK tools in the Java " r"installation '(.*)' used by Gradle. Make sure Gradle is running " "on a JDK, not JRE.", lambda m: MissingJDK(m.group(1)), ), ( r"\> JDK_5 environment variable is not defined. " r"It must point to any JDK that is capable to compile with " r"Java 5 target \((.*)\)", lambda m: MissingJDK(m.group(1)), ), ( r"ERROR: JAVA_HOME is not set and no 'java' command could be found " r"in your PATH.", lambda m: MissingJRE(), ), ( r"Error: environment variable \"JAVA_HOME\" must be set to a JDK " r"\(>= v(.*)\) installation directory", lambda m: MissingJDK(m.group(1)), ), ( r"(?:/usr/bin/)?install: cannot create regular file \'(.*)\': " r"No such file or directory", None, ), ( r"python[0-9.]*: can\'t open file \'(.*)\': \[Errno 2\] " r"No such file or directory", file_not_found, ), ( r"error: \[Errno 2\] No such file or directory: '(.*)'", file_not_found_maybe_executable, ), ( r".*:[0-9]+:[0-9]+: ERROR: \ " r"\[\'/usr/bin/python3\'\]\> is not a valid python or " r"it is missing setuptools", lambda m: MissingPythonDistribution("setuptools", python_version=3), ), (r"OSError: \[Errno 28\] No space left on device", lambda m: NoSpaceOnDevice()), # python:setuptools_scm ( r'LookupError: setuptools-scm was unable to detect version for \'.*\'\.', lambda m: SetuptoolScmVersionIssue() ), ( r'LookupError: setuptools-scm was unable to detect version for .*\.', lambda m: SetuptoolScmVersionIssue(), ), (r"OSError: 'git' was not found", lambda m: MissingCommand("git")), (r"OSError: No such file (.*)", file_not_found_maybe_executable), ( r"Could not open \'(.*)\': No such file or directory at " r"\/usr\/share\/perl\/[0-9.]+\/ExtUtils\/MM_Unix.pm line [0-9]+.", lambda m: MissingPerlFile(m.group(1)) ), ( r'Can\'t open perl script "(.*)": No such file or directory', lambda m: MissingPerlFile(m.group(1)) ), ( MAVEN_ERROR_PREFIX + r"Failed to execute goal on project .*: " "\x1b\\[1;31mCould not resolve dependencies for project .*: " "The following artifacts could not be resolved: (.*): " "Could not find artifact (.*) in (.*) \\((.*)\\)\x1b\\[m -> \x1b\\[1m\\[Help 1\\]\x1b\\[m", maven_missing_artifact, ), # Maven ( MAVEN_ERROR_PREFIX + r"Failed to execute goal on project .*: " r"Could not resolve dependencies for project .*: " r"The following artifacts could not be resolved: (.*): " r"Cannot access central \(https://repo\.maven\.apache\.org/maven2\) " r"in offline mode and the artifact .* has not been downloaded from " r"it before..*", maven_missing_artifact, ), ( MAVEN_ERROR_PREFIX + r"Unresolveable build extension: " r"Plugin (.*) or one of its dependencies could not be resolved: " r"Cannot access central \(https://repo.maven.apache.org/maven2\) " r"in offline mode and the artifact .* has not been downloaded " "from it before. @", lambda m: MissingMavenArtifacts([m.group(1)]) ), ( MAVEN_ERROR_PREFIX + r"Non-resolvable import POM: Cannot access central " r"\(https://repo.maven.apache.org/maven2\) in offline mode and the " r"artifact (.*) has not been downloaded from it before. " r"@ line [0-9]+, column [0-9]+", maven_missing_artifact, ), ( r"\[FATAL\] Non-resolvable parent POM for .*: Cannot access central " r"\(https://repo.maven.apache.org/maven2\) in offline mode and the " "artifact (.*) has not been downloaded from it before. .*", maven_missing_artifact, ), ( MAVEN_ERROR_PREFIX + r"Plugin (.*) or one of its dependencies could " r"not be resolved: Cannot access central " r"\(https://repo.maven.apache.org/maven2\) in offline mode and the " r"artifact .* has not been downloaded from it before. -> \[Help 1\]", lambda m: MissingMavenArtifacts([m.group(1)]) ), ( MAVEN_ERROR_PREFIX + r"Failed to execute goal on project .*: " r"Could not resolve dependencies for project .*: Cannot access " r".* \([^\)]+\) in offline mode and the artifact " r"(.*) has not been downloaded from it before. -> \[Help 1\]", maven_missing_artifact, ), ( MAVEN_ERROR_PREFIX + r"Failed to execute goal on project .*: " r"Could not resolve dependencies for project .*: Cannot access central " r"\(https://repo.maven.apache.org/maven2\) in offline mode and the " r"artifact (.*) has not been downloaded from it before..*", maven_missing_artifact, ), (MAVEN_ERROR_PREFIX + "Failed to execute goal (.*) on project (.*): (.*)", None), ( MAVEN_ERROR_PREFIX + r"Error resolving version for plugin \'(.*)\' from the repositories " r"\[.*\]: Plugin not found in any plugin repository -> \[Help 1\]", lambda m: MissingMavenArtifacts([m.group(1)]) ), ( r'E: eatmydata: unable to find \'(.*)\' in PATH', lambda m: MissingCommand(m.group(1)), ), ( r'\'(.*)\' not found in PATH at (.*) line ([0-9]+)\.', lambda m: MissingCommand(m.group(1)), ), ( r'/usr/bin/eatmydata: [0-9]+: exec: (.*): not found', command_missing ), ( r"(.*): exec: \"(.*)\": executable file not found in \$PATH", lambda m: MissingCommand(m.group(2)), ), ( r"Can't exec \"(.*)\": No such file or directory at (.*) line ([0-9]+)\.", command_missing, ), ( r"dh_missing: (warning: )?(.*) exists in debian/.* but is not " r"installed to anywhere", lambda m: DhMissingUninstalled(m.group(2)), ), (r"dh_link: link destination (.*) is a directory", lambda m: DhLinkDestinationIsDirectory(m.group(1))), (r"I/O error : Attempt to load network entity (.*)", lambda m: MissingXmlEntity(m.group(1))), (r"ccache: error: (.*)", lambda m: CcacheError(m.group(1))), ( r"dh: The --until option is not supported any longer \(#932537\). " r"Use override targets instead.", lambda m: DhUntilUnsupported(), ), ( r"dh: unable to load addon (.*): (.*) did not return a true " r"value at \(eval 11\) line ([0-9]+).", lambda m: DhAddonLoadFailure(m.group(1), m.group(2)), ), ( "ERROR: dependencies (.*) are not available for package [‘'](.*)['’]", r_missing_package, ), ( "ERROR: dependency [‘'](.*)['’] is not available for package [‘'](.*)[’']", r_missing_package, ), ( r"Error in library\(.*\) : there is no package called \'(.*)\'", r_missing_package, ), (r'Error in .* : there is no package called \'(.*)\'', r_missing_package), (r"there is no package called \'(.*)\'", r_missing_package), ( r" namespace ‘(.*)’ ([^ ]+) is being loaded, but >= ([^ ]+) is required", lambda m: MissingRPackage(m.group(1), m.group(3)) ), ( r" namespace ‘(.*)’ ([^ ]+) is already loaded, but >= ([^ ]+) " r"is required", lambda m: MissingRPackage(m.group(1), m.group(3)) ), (r'b\'convert convert: ' r'Unable to read font \((.*)\) \[No such file or directory\]\.\\n\'', file_not_found), (r"mv: cannot stat \'(.*)\': No such file or directory", file_not_found), (r"mv: cannot move \'.*\' to \'(.*)\': No such file or directory", None), ( r"(/usr/bin/install|mv): " r"will not overwrite just-created \'(.*)\' with \'(.*)\'", None, ), (r"IOError: \[Errno 2\] No such file or directory: \'(.*)\'", file_not_found_maybe_executable), (r"error: \[Errno 2\] No such file or directory: \'(.*)\'", file_not_found_maybe_executable), (r"E IOError: \[Errno 2\] No such file or directory: \'(.*)\'", file_not_found_maybe_executable), ("FAIL\t(.+\\/.+\\/.+)\t([0-9.]+)s", None), ( r'dh_(.*): Cannot find \(any matches for\) "(.*)" \(tried in (.*)\)', lambda m: DebhelperPatternNotFound( m.group(2), m.group(1), [d.strip() for d in m.group(3).split(",")]) ), ( r'Can\'t exec "(.*)": No such file or directory at ' r"/usr/share/perl5/Debian/Debhelper/Dh_Lib.pm line [0-9]+.", command_missing, ), (r".*: error: (.*) command not found", command_missing), (r'error: command \'(.*)\' failed: No such file or directory', command_missing), ( r"dh_install: Please use dh_missing " "--list-missing/--fail-missing instead", None, ), ( r'dh([^:]*): Please use the third-party "pybuild" build system ' "instead of python-distutils", None, ), # A Python error, but not likely to be actionable. The previous # line will have the actual line that failed. (r"ImportError: cannot import name (.*)", None), # Rust ? (r"\s*= note: /usr/bin/ld: cannot find -l([^ ]+): .*", lambda m: MissingLibrary(m.group(1))), (r"\s*= note: /usr/bin/ld: cannot find -l([^ ]+)", lambda m: MissingLibrary(m.group(1))), (r"/usr/bin/ld: cannot find -l([^ ]+): .*", lambda m: MissingLibrary(m.group(1))), (r"/usr/bin/ld: cannot find -l([^ ]+)", lambda m: MissingLibrary(m.group(1))), ( r"Could not find gem \'([^ ]+) \(([^)]+)\)\', " r"which is required by gem.*", ruby_missing_gem, ), ( r"Could not find gem \'([^ \']+)\', " r"which is required by gem.*", lambda m: MissingRubyGem(m.group(1)), ), ( r"[^:]+:[0-9]+:in \`to_specs\': Could not find \'(.*)\' \(([^)]+)\) " r"among [0-9]+ total gem\(s\) \(Gem::MissingSpecError\)", ruby_missing_gem, ), ( r"[^:]+:[0-9]+:in \`to_specs\': Could not find \'(.*)\' \(([^)]+)\) " r"- .* \(Gem::MissingSpecVersionError\)", ruby_missing_gem, ), ( r"[^:]+:[0-9]+:in \`block in verify_gemfile_dependencies_are_found\!\': " r"Could not find gem \'(.*)\' in any of the gem sources listed in " r"your Gemfile\. \(Bundler::GemNotFound\)", lambda m: MissingRubyGem(m.group(1)), ), ( r"Exception: (.*) not in path[!.]*", lambda m: MissingCommand(m.group(1)) ), ( r"[^:]+:[0-9]+:in \`find_spec_for_exe\': can\'t find gem " r"(.*) \(([^)]+)\) with executable (.*) \(Gem::GemNotFoundException\)", ruby_missing_gem, ), ( r"PHP Fatal error: Uncaught Error: Class \'(.*)\' not found in " r"(.*):([0-9]+)", lambda m: MissingPhpClass(m.group(1)) ), (r"Caused by: java.lang.ClassNotFoundException: (.*)", lambda m: MissingJavaClass(m.group(1))), ( r"\[(.*)\] \t\t:: (.*)\#(.*);\$\{(.*)\}: not found", lambda m: MissingMavenArtifacts( ["%s:%s:jar:debian" % (m.group(2), m.group(3))] ), ), ( r"Caused by: java.lang.IllegalArgumentException: " r"Cannot find JAR \'(.*)\' required by module \'(.*)\' " r"using classpath or distribution directory \'(.*)\'", None, ), ( r".*\.xml:[0-9]+: Unable to find a javac compiler;", lambda m: MissingJavaClass("com.sun.tools.javac.Main"), ), ( r'checking for (.*)\.\.\. configure: error: "Cannot check for existence of module (.*) without pkgconf"', lambda m: MissingCommand("pkgconf"), ), ( r'configure: error: Could not find \'(.*)\' in path\.', lambda m: MissingCommand(m.group(1)), ), ( r"autoreconf was not found; .*", lambda m: MissingCommand("autoreconf"), ), (r"g\+\+: error: (.*): No such file or directory", file_not_found), (r"strip: \'(.*)\': No such file", file_not_found), ( r"Sprockets::FileNotFound: couldn\'t find file \'(.*)\' " r"with type \'(.*)\'", lambda m: MissingSprocketsFile(m.group(1), m.group(2)), ), ( r'xdt-autogen: You must have "(.*)" installed. You can get if from', lambda m: MissingXfceDependency(m.group(1)), ), ( r"autogen.sh: You must have GNU autoconf installed.", lambda m: MissingCommand("autoconf"), ), ( r"\s*You must have (autoconf|automake|aclocal|libtool|libtoolize) installed to compile (.*)\.", lambda m: MissingCommand(m.group(1)), ), ( r"It appears that Autotools is not correctly installed on this system.", lambda m: MissingCommand("autoconf"), ), ( r"\*\*\* No autoreconf found \*\*\*", lambda m: MissingCommand("autoreconf"), ), (r"You need to install gnome-common module and make.*", lambda m: GnomeCommonMissing()), (r"You need to install the gnome-common module and make.*", lambda m: GnomeCommonMissing()), ( r"You need to install gnome-common from the GNOME (git|CVS|SVN)", lambda m: GnomeCommonMissing(), ), ( r"automake: error: cannot open < (.*): No such file or directory", lambda m: MissingAutomakeInput(m.group(1)), ), ( r"configure.(in|ac):[0-9]+: error: possibly undefined macro: (.*)", lambda m: MissingAutoconfMacro(m.group(2)), ), ( r"configure.(in|ac):[0-9]+: error: macro (.*) is not defined; " r"is a m4 file missing\?", lambda m: MissingAutoconfMacro(m.group(2)), ), ( r"config.status: error: cannot find input file: `(.*)\'", lambda m: MissingConfigStatusInput(m.group(1)), ), ( r"\*\*\*Error\*\*\*: You must have glib-gettext >= (.*) installed.*", lambda m: MissingGnomeCommonDependency("glib-gettext", m.group(1)), ), ( r"ERROR: JAVA_HOME is set to an invalid directory: " r"/usr/lib/jvm/default-java/", lambda m: MissingJVM(), ), ( r'Error: The file "MANIFEST" is missing from this distribution\. ' r'The MANIFEST lists all files included in the distribution\.', lambda m: MissingPerlManifest() ), ( r"dh_installdocs: --link-doc not allowed between (.*) and (.*) " r"\(one is arch:all and the other not\)", None, ), ( r"dh: unable to load addon systemd: dh: The systemd-sequence is " "no longer provided in compat >= 11, please rely on dh_installsystemd " "instead", None, ), ( r"dh: The --before option is not supported any longer \(#932537\). " r"Use override targets instead.", None, ), ("(.*):([0-9]+): undefined reference to `(.*)'", None), ("(.*):([0-9]+): error: undefined reference to '(.*)'", None), ( r"\/usr\/bin\/ld:(.*): multiple definition of `*.\'; " r"(.*): first defined here", None, ), (r".+\.go:[0-9]+: undefined reference to `(.*)'", None), (r"ar: libdeps specified more than once", None), ( r"\/usr\/bin\/ld: .*\(.*\):\(.*\): multiple definition of `*.\'; " r"(.*):\((.*)\) first defined here", None, ), ( r"\/usr\/bin\/ld:(.*): multiple definition of `*.\'; " r"(.*):\((.*)\) first defined here", None, ), (r"\/usr\/bin\/ld: (.*): undefined reference to `(.*)\'", None), (r"\/usr\/bin\/ld: (.*): undefined reference to symbol \'(.*)\'", None), ( r"\/usr\/bin\/ld: (.*): relocation (.*) against symbol `(.*)\' " r"can not be used when making a shared object; recompile with -fPIC", None, ), ( "(.*):([0-9]+): multiple definition of `(.*)'; (.*):([0-9]+): " "first defined here", None, ), ( "(dh.*): debhelper compat level specified both in debian/compat " "and via build-dependency on debhelper-compat", lambda m: DuplicateDHCompatLevel(m.group(1)), ), ( "(dh.*): (error: )?Please specify the compatibility level in " "debian/compat", lambda m: MissingDHCompatLevel(m.group(1)), ), ( "dh_makeshlibs: The udeb (.*) does not contain any shared libraries " "but --add-udeb=(.*) was passed!?", None, ), ( "dpkg-gensymbols: error: some symbols or patterns disappeared in the " "symbols file: see diff output below", lambda m: DisappearedSymbols(), ), ( r"Failed to copy \'(.*)\': No such file or directory at " r"/usr/share/dh-exec/dh-exec-install-rename line [0-9]+.*", file_not_found, ), (r"Invalid gemspec in \[.*\]: No such file or directory - (.*)", command_missing), ( r".*meson.build:[0-9]+:[0-9]+: ERROR: Program\(s\) \[\'(.*)\'\] not " r"found or not executable", command_missing, ), ( r".*meson.build:[0-9]+:[0-9]: ERROR: Git program not found\.", lambda m: MissingCommand("git"), ), ( r"Failed: [pytest] section in setup.cfg files is no longer " r"supported, change to [tool:pytest] instead.", None, ), (r"cp: cannot stat \'(.*)\': No such file or directory", None), (r"cp: \'(.*)\' and \'(.*)\' are the same file", None), (r"PHP Fatal error: (.*)", None), (r"sed: no input files", None), (r"sed: can\'t read (.*): No such file or directory", file_not_found), ( r"ERROR in Entry module not found: Error: Can\'t resolve " r"\'(.*)\' in \'(.*)\'", webpack_file_missing, ), ( r".*:([0-9]+): element include: XInclude error : " r"could not load (.*), and no fallback was found", None, ), (r"E: Child terminated by signal ‘Terminated’", lambda m: Cancelled(), ), (r"E: Caught signal ‘Terminated’", lambda m: Cancelled(), ), (r"E: Failed to execute “(.*)”: No such file or directory", command_missing), (r"E ImportError: Bad (.*) executable", command_missing), (r"E: The Debian version .* cannot be used as an ELPA version.", None), # ImageMagick ( r"convert convert: Image pixel limit exceeded " r"\(see -limit Pixels\) \(-1\).", None, ), (r"convert convert: Improper image header \(.*\).", None), (r"convert convert: invalid primitive argument \([0-9]+\).", None), (r"convert convert: Unexpected end-of-file \(\)\.", None), (r"convert convert: Unrecognized option \((.*)\)\.", None), (r"convert convert: Unrecognized channel type \((.*)\)\.", None), ( r"convert convert: Unable to read font \((.*)\) " r"\[No such file or directory\].", file_not_found, ), ( r"convert convert: Unable to open file (.*) \[No such file or directory\]\.", file_not_found, ), ( r"convert convert: No encode delegate for this image format \((.*)\) " r"\[No such file or directory\].", lambda m: ImageMagickDelegateMissing(m.group(1)), ), (r"ERROR: Sphinx requires at least Python (.*) to run.", None), (r"Can\'t find (.*) directory in (.*)", None), ( r"/bin/sh: [0-9]: cannot create (.*): Directory nonexistent", lambda m: DirectoryNonExistant(os.path.dirname(m.group(1))), ), (r"dh: Unknown sequence (.*) \(choose from: .*\)", None), (r".*\.vala:[0-9]+\.[0-9]+-[0-9]+.[0-9]+: error: (.*)", None), ( r"error: Package `(.*)\' not found in specified Vala API directories " r"or GObject-Introspection GIR directories", lambda m: MissingValaPackage(m.group(1)), ), (r".*.scala:[0-9]+: error: (.*)", None), # JavaScript (r"error TS6053: File \'(.*)\' not found.", file_not_found), # Mocha (r"Error \[ERR_MODULE_NOT_FOUND\]: Cannot find package '(.*)' " "imported from (.*)", lambda m: MissingNodeModule(m.group(1))), (r"(.*\.ts)\([0-9]+,[0-9]+\): error TS[0-9]+: (.*)", None), (r"(.*.nim)\([0-9]+, [0-9]+\) Error: .*", None), ( r"dh_installinit: upstart jobs are no longer supported\! " r"Please remove (.*) and check if you need to add a conffile removal", lambda m: UpstartFilePresent(m.group(1)), ), ( r"dh_installinit: --no-restart-on-upgrade has been renamed to " "--no-stop-on-upgrade", None, ), (r"find: paths must precede expression: .*", None), (r"find: ‘(.*)’: No such file or directory", file_not_found), (r"ninja: fatal: posix_spawn: Argument list too long", None), ("ninja: fatal: chdir to '(.*)' - No such file or directory", lambda m: DirectoryNonExistant(m.group(1))), # Java (r"error: Source option [0-9] is no longer supported. Use [0-9] or later.", None), ( r"(dh.*|jh_build): -s/--same-arch has been removed; " r"please use -a/--arch instead", None, ), ( r"dh_systemd_start: dh_systemd_start is no longer used in " r"compat >= 11, please use dh_installsystemd instead", None, ), (r"Trying patch (.*) at level 1 \.\.\. 0 \.\.\. 2 \.\.\. failure.", None), # QMake (r"Project ERROR: Unknown module\(s\) in QT: (.*)", None), (r"Project ERROR: (.*) development package not found", pkg_config_missing), (r"Package \'(.*)\', required by \'(.*)\', not found\n", pkg_config_missing), (r"pkg-config cannot find (.*)", pkg_config_missing), ( r"configure: error: .* not found: Package dependency requirement " r"\'([^\']+)\' could not be satisfied.", pkg_config_missing, ), ( r"configure: error: (.*) is required to build documentation", lambda m: MissingVagueDependency(m.group(1)), ), (r".*:[0-9]+: (.*) does not exist.", file_not_found), # uglifyjs (r"ERROR: can\'t read file: (.*)", file_not_found), (r'jh_build: Cannot find \(any matches for\) "(.*)" \(tried in .*\)', None), ( r"-- Package \'(.*)\', required by \'(.*)\', not found", lambda m: MissingPkgConfig(m.group(1)), ), ( r".*.rb:[0-9]+:in `require_relative\': cannot load such file " r"-- (.*) \(LoadError\)", None, ), ( r".*.rb:[0-9]+:in `require\': cannot load such file " r"-- (.*) \(LoadError\)", lambda m: MissingRubyFile(m.group(1)), ), (r"LoadError: cannot load such file -- (.*)", lambda m: MissingRubyFile(m.group(1))), (r" cannot load such file -- (.*)", lambda m: MissingRubyFile(m.group(1))), # TODO(jelmer): This is a fairly generic string; perhaps combine with other # checks for ruby? (r"File does not exist: ([a-z/]+)$", lambda m: MissingRubyFile(m.group(1))), ( r".*:[0-9]+:in `do_check_dependencies\': E: " r"dependency resolution check requested but no working " r"gemspec available \(RuntimeError\)", None, ), (r"rm: cannot remove \'(.*)\': Is a directory", None), (r"rm: cannot remove \'(.*)\': No such file or directory", None), # Invalid option from Python (r"error: option .* not recognized", None), # Invalid option from go (r"flag provided but not defined: .*", None), (r'CMake Error: The source directory "(.*)" does not exist.', lambda m: DirectoryNonExistant(m.group(1))), (r".*: [0-9]+: cd: can\'t cd to (.*)", lambda m: DirectoryNonExistant(m.group(1))), (r"/bin/sh: 0: Can\'t open (.*)", file_not_found_maybe_executable), (r"/bin/sh: [0-9]+: cannot open (.*): No such file", file_not_found_maybe_executable), (r".*: line [0-9]+: (.*): No such file or directory", file_not_found_maybe_executable), (r"/bin/sh: [0-9]+: Syntax error: .*", None), (r"error: No member named \$memberName", None), ( r"(?:/usr/bin/)?install: cannot create regular file \'(.*)\': " r"Permission denied", None, ), (r"(?:/usr/bin/)?install: cannot create directory .(.*).: File exists", None), (r"/usr/bin/install: missing destination file operand after .*", None), # Ruby (r"rspec .*\.rb:[0-9]+ # (.*)", None), # help2man (r"Addendum (.*) does NOT apply to (.*) \(translation discarded\).", None), ( r"dh_installchangelogs: copy\((.*), (.*)\): No such file or directory", file_not_found, ), (r"dh_installman: mv (.*) (.*): No such file or directory", file_not_found), (r"dh_installman: Could not determine section for (.*)", None), ( r"failed to initialize build cache at (.*): mkdir (.*): " r"permission denied", None, ), ( r'Can\'t exec "(.*)": No such file or directory at (.*) line ([0-9]+).', command_missing, ), ( r'E OSError: No command "(.*)" found on host .*', command_missing ), # PHPUnit (r'Cannot open file "(.*)".', file_not_found), ( r".*Could not find a JavaScript runtime\. See " r"https://github.com/rails/execjs for a list of available runtimes\..*", lambda m: MissingJavaScriptRuntime(), ), PythonFileNotFoundErrorMatcher(), # ruby (r"Errno::ENOENT: No such file or directory - (.*)", file_not_found), (r"(.*.rb):[0-9]+:in `.*\': .* \(.*\) ", None), # JavaScript (r".*: ENOENT: no such file or directory, open \'(.*)\'", file_not_found), (r"\[Error: ENOENT: no such file or directory, stat \'(.*)\'\] \{", file_not_found), ( r"(.*):[0-9]+: error: Libtool library used but \'LIBTOOL\' is undefined", lambda m: MissingLibtool(), ), # libtoolize (r"libtoolize: error: \'(.*)\' does not exist.", file_not_found), # Seen in python-cogent ( "(OSError|RuntimeError): (.*) required but not found.", lambda m: MissingVagueDependency(m.group(2)) ), ( r'RuntimeError: The (.*) executable cannot be found\. ' r'Please check if it is in the system path\.', lambda m: MissingCommand(m.group(1).lower()) ), ( r'Cannot find Git. Git is required for .*', lambda m: MissingCommand('git') ), ( r'E ImportError: Bad (.*) executable\.', lambda m: MissingCommand('git') ), ( "RuntimeError: (.*) is missing", lambda m: MissingVagueDependency(m.group(1)), ), ( r"(OSError|RuntimeError): Could not find (.*) library\..*", lambda m: MissingVagueDependency(m.group(2)) ), ( r'(OSError|RuntimeError): We need package (.*), but not importable', lambda m: MissingPythonDistribution(m.group(2)) ), ( r'(OSError|RuntimeError): No (.*) was found: .*', lambda m: MissingVagueDependency(m.group(2)) ), # Seen in cpl-plugin-giraf ( r"ImportError: Numpy version (.*) or later must be " r"installed to use .*", lambda m: MissingPythonModule("numpy", minimum_version=m.group(1)), ), # Seen in mayavi2 (r"\w+Numpy is required to build.*", lambda m: MissingPythonModule("numpy")), # autoconf (r"configure.ac:[0-9]+: error: required file \'(.*)\' not found", file_not_found), (r'/usr/bin/m4:(.*):([0-9]+): cannot open `(.*)\': ' r'No such file or directory', lambda m: MissingFile(m.group(3))), # automake (r"Makefile.am: error: required file \'(.*)\' not found", file_not_found), # sphinx (r"config directory doesn\'t contain a conf.py file \((.*)\)", None), # vcversioner ( r"vcversioner: no VCS could be detected in \'/<>\' " r"and \'/<>/version.txt\' isn\'t present.", None, ), # rst2html (and other Python?) (r" InputError: \[Errno 2\] No such file or directory: \'(.*)\'", file_not_found), # gpg (r"gpg: can\'t connect to the agent: File name too long", None), (r"(.*.lua):[0-9]+: assertion failed", None), (r"\s+\^\-\-\-\-\^ SC[0-4][0-9][0-9][0-9]: .*", None), ( r"Error: (.*) needs updating from (.*)\. " r"Run \'pg_buildext updatecontrol\'.", lambda m: NeedPgBuildExtUpdateControl(m.group(1), m.group(2)), ), (r"Patch (.*) does not apply \(enforce with -f\)", lambda m: PatchApplicationFailed(m.group(1))), ( r"java.io.FileNotFoundException: (.*) \(No such file or directory\)", file_not_found, ), # Pytest (r"INTERNALERROR> PluginValidationError: (.*)", None), (r"[0-9]+ out of [0-9]+ hunks FAILED -- saving rejects to file (.*\.rej)", None), (r"pkg_resources.UnknownExtra: (.*) has no such extra feature \'(.*)\'", None), ( r"dh_auto_configure: invalid or non-existing path " r"to the source directory: .*", None, ), # Sphinx ( r"sphinx_rtd_theme is no longer a hard dependency since version (.*). " r"Please install it manually.\(pip install (.*)\)", lambda m: MissingPythonModule("sphinx_rtd_theme"), ), (r"There is a syntax error in your configuration file: (.*)", None), ( r"E: The Debian version (.*) cannot be used as an ELPA version.", lambda m: DebianVersionRejected(m.group(1)), ), (r'"(.*)" is not exported by the ExtUtils::MakeMaker module', None), ( r"E: Please add appropriate interpreter package to Build-Depends, " r"see pybuild\(1\) for details\..*", lambda m: DhAddonLoadFailure("pybuild", "Debian/Debhelper/Buildsystem/pybuild.pm"), ), (r"dpkg: error: .*: No space left on device", lambda m: NoSpaceOnDevice()), ( r"You need the GNU readline library\(ftp://ftp.gnu.org/gnu/readline/\s+\) " r"to build", lambda m: MissingLibrary("readline"), ), ( r'configure: error: Could not find lib(.*)', lambda m: MissingLibrary(m.group(1)) ), ( r" Could not find module ‘(.*)’", lambda m: MissingHaskellModule(m.group(1)), ), (r'E: session: (.*): Chroot not found', lambda m: ChrootNotFound(m.group(1))), HaskellMissingDependencyMatcher(), SetupPyCommandMissingMatcher(), CMakeErrorMatcher(), ( r"error: failed to select a version for the requirement `(.*)`", cargo_missing_requirement, ), (r"^Environment variable \$SOURCE_DATE_EPOCH: No digits were found: $", None), ( r"\[ERROR\] LazyFont - Failed to read font file (.*) " r"\" r"java.io.FileNotFoundException: (.*) \(No such file or directory\)", lambda m: MissingFile(m.group(1)), ), (r"qt.qpa.xcb: could not connect to display", lambda m: MissingXDisplay()), (r'\(.*:[0-9]+\): Gtk-WARNING \*\*: [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}: cannot open display: ', lambda m: MissingXDisplay()), ( r"Package (.*) was not found in the pkg-config search path.", lambda m: MissingPkgConfig(m.group(1)), ), ( r"Can't open display", lambda m: MissingXDisplay(), ), ( r'pkg-config does not know (.*) at .*\.', lambda m: MissingPkgConfig(m.group(1)), ), ( r'\*\*\* Please install (.*) \(atleast version (.*)\) or adjust', lambda m: MissingPkgConfig(m.group(1), m.group(2)) ), ( r"go runtime is required: https://golang.org/doc/install", lambda m: MissingGoRuntime(), ), ( r"\%Error: '(.*)' must be installed to build", lambda m: MissingCommand(m.group(1)), ), ( r'configure: error: "Could not find (.*) in PATH"', lambda m: MissingCommand(m.group(1)), ), ( r'Could not find executable (.*)', lambda m: MissingCommand(m.group(1)) ), ( r"go: .*: Get \"(.*)\": x509: certificate signed by unknown authority", lambda m: UnknownCertificateAuthority(m.group(1)), ), ( r".*.go:[0-9]+:[0-9]+: .*: Get \"(.*)\": x509: certificate signed by unknown authority", lambda m: UnknownCertificateAuthority(m.group(1)), ), ( r"fatal: unable to access '(.*)': server certificate verification failed. CAfile: none CRLfile: none", lambda m: UnknownCertificateAuthority(m.group(1)), ), ( r'curl: \(77\) error setting certificate verify locations: CAfile: (.*) CApath: (.*)', lambda m: MissingFile(m.group(1)) ), ( r"\t\(Do you need to predeclare (.*)\?\)", lambda m: MissingPerlPredeclared(m.group(1)), ), ( r"Bareword \"(.*)\" not allowed while \"strict subs\" in use at " r"Makefile.PL line ([0-9]+).", lambda m: MissingPerlPredeclared(m.group(1)), ), ( r'String found where operator expected at Makefile.PL line ([0-9]+), ' 'near "([a-z0-9_]+).*"', lambda m: MissingPerlPredeclared(m.group(2)), ), (r" vignette builder 'knitr' not found", lambda m: MissingRPackage("knitr")), ( r"fatal: unable to auto-detect email address \(got \'.*\'\)", lambda m: MissingGitIdentity(), ), ( r"E fatal: unable to auto-detect email address \(got \'.*\'\)", lambda m: MissingGitIdentity(), ), (r"gpg: no default secret key: No secret key", lambda m: MissingSecretGpgKey()), ( r"ERROR: FAILED--Further testing stopped: " r"Test requires module \'(.*)\' but it\'s not found", lambda m: MissingPerlModule(None, m.group(1)), ), ( r"(subprocess.CalledProcessError|error): " r"Command \'\[\'/usr/bin/python([0-9.]*)\', \'-m\', \'pip\', " r"\'--disable-pip-version-check\', \'wheel\', \'--no-deps\', \'-w\', " r".*, \'([^-][^\']+)\'\]\' " r"returned non-zero exit status 1.", lambda m: MissingPythonDistribution.from_requirement_str( m.group(3), python_version=(int(m.group(2)[0]) if m.group(2) else None) ), ), ( r"vcversioner: \[\'git\', .*, \'describe\', \'--tags\', \'--long\'\] " r"failed and \'(.*)/version.txt\' isn\'t present\.", lambda m: MissingVcVersionerVersion(), ), ( r"vcversioner: no VCS could be detected in '(.*)' and " r"'(.*)/version.txt' isn't present\.", lambda m: MissingVcVersionerVersion(), ), ( r"You don't have a working TeX binary \(tex\) installed anywhere in", lambda m: MissingCommand("tex"), ), ( r"# Module \'(.*)\' is not installed", lambda m: MissingPerlModule(None, m.group(1)), ), ( r'Base class package "(.*)" is empty.', lambda m: MissingPerlModule(None, m.group(1)), ), ( r" \! (.*::.*) is not installed", lambda m: MissingPerlModule(None, m.group(1)), ), ( r'Cannot find (.*) in @INC at (.*) line ([0-9]+)\.', lambda m: MissingPerlModule(None, m.group(1)), ), ( r'(.*::.*) (.*) is required to configure our .* dependency, ' r'please install it manually or upgrade your CPAN/CPANPLUS', lambda m: MissingPerlModule(None, m.group(1), minimum_version=m.group(2)) ), ( r"configure: error: Missing lib(.*)\.", lambda m: MissingLibrary(m.group(1)), ), ( r"OSError: (.*): cannot open shared object file: No such file or directory", lambda m: MissingFile(m.group(1)), ), ( r'The "(.*)" executable has not been found\.', lambda m: MissingCommand(m.group(1)), ), ( r" '\! LaTeX Error: File `(.*)' not found.'", lambda m: MissingLatexFile(m.group(1)), ), ( r"\! LaTeX Error: File `(.*)\' not found\.", lambda m: MissingLatexFile(m.group(1)), ), ( r"(\!|.*:[0-9]+:) Package fontspec Error: The font \"(.*)\" cannot be found\.", lambda m: MissingFontspec(m.group(2)), ), (r" vignette builder \'(.*)\' not found", lambda m: MissingRPackage(m.group(1))), ( r"Error: package [‘'](.*)[’'] (.*) was found, but >= (.*) is required by [‘'](.*)[’']", lambda m: MissingRPackage(m.group(1), m.group(3)), ), ( r'there is no package called \'(.*)\'', lambda m: MissingRPackage(m.group(1)) ), (r"Error in .*: there is no package called ‘(.*)’", lambda m: MissingRPackage(m.group(1))), (r" there is no package called \'(.*)\'", lambda m: MissingRPackage(m.group(1))), ( r"Exception: cannot execute command due to missing interpreter: (.*)", command_missing, ), ( r'E: Build killed with signal TERM after ([0-9]+) minutes of inactivity', lambda m: InactiveKilled(int(m.group(1))) ), (r'\[.*Authority\] PAUSE credentials not found in "config.ini" or "dist.ini" or "~/.pause"\! ' r'Please set it or specify an authority for this plugin. at inline delegation in ' r'Dist::Zilla::Plugin::Authority for logger->log_fatal \(attribute declared in ' r'/usr/share/perl5/Dist/Zilla/Role/Plugin.pm at line [0-9]+\) line [0-9]+\.', lambda m: MissingPauseCredentials()), ( r'npm ERR\! ERROR: \[Errno 2\] No such file or directory: \'(.*)\'', file_not_found ), ( r'\*\*\* error: gettext infrastructure mismatch: using a Makefile\.in\.in ' r'from gettext version ([0-9.]+) but the autoconf macros are from gettext ' r'version ([0-9.]+)', lambda m: MismatchGettextVersions(m.group(1), m.group(2))), ( r'You need to install the (.*) package to use this program\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r'You need to install (.*)', lambda m: MissingVagueDependency(m.group(1))), ( r'configure: error: You need (.*) installed', lambda m: MissingVagueDependency(m.group(1)) ), ( r'open3: exec of cme (.*) failed: No such file or directory ' r'at .*/Dist/Zilla/Plugin/Run/Role/Runner.pm line [0-9]+\.', lambda m: MissingPerlModule(None, 'App::Cme::Command::' + m.group(1)) ), ( r'([^ ]+) \(for section ([^ ]+)\) does not appear to be installed', lambda m: MissingPerlModule(None, m.group(1))), ( r'(.*) version (.*) required--this is only version (.*) ' r'at .*\.pm line [0-9]+\.', lambda m: MissingPerlModule(None, m.group(1), minimum_version=m.group(2)), ), ( r'Bailout called\. Further testing stopped: ' r'YOU ARE MISSING REQUIRED MODULES: \[ ([^,]+)(.*) \]:', lambda m: MissingPerlModule(None, m.group(1)) ), ( r'CMake Error: CMake was unable to find a build program corresponding' r' to "(.*)". CMAKE_MAKE_PROGRAM is not set\. You probably need to ' r'select a different build tool\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r"Dist currently only works with Git or Mercurial repos", lambda m: VcsControlDirectoryNeeded(['git', 'hg']), ), ( r'GitHubMeta: need a .git\/config file, and you don\'t have one', lambda m: VcsControlDirectoryNeeded(['git']) ), ( r"Exception: Versioning for this project requires either an sdist " r"tarball, or access to an upstream git repository\. It's also " r"possible that there is a mismatch between the package name " r"in setup.cfg and the argument given to pbr\.version\.VersionInfo\. " r"Project name .* was given, but was not able to be found\.", lambda m: VcsControlDirectoryNeeded(["git"]) ), (r'configure: error: no suitable Python interpreter found', lambda m: MissingCommand('python')), (r' Failed to find (.*) development headers\.', lambda m: MissingVagueDependency(m.group(1))), (r'\*\*\* \Subdirectory \'(.*)\' does not yet exist. ' r'Use \'./gitsub.sh pull\' to create it, or set the ' r'environment variable GNULIB_SRCDIR\.', lambda m: MissingGnulibDirectory(m.group(1)) ), (r'configure: error: Cap\'n Proto compiler \(capnp\) not found.', lambda m: MissingCommand('capnp')), (r'lua: (.*):(\d+): module \'(.*)\' not found:', lambda m: MissingLuaModule(m.group(3))), (r'Unknown key\(s\) in sphinx_gallery_conf:', None), (r'(.+\.gir):In (.*): error: (.*)', None), (r'(.+\.gir):[0-9]+\.[0-9]+-[0-9]+\.[0-9]+: error: (.*)', None), (r'psql:.*\.sql:[0-9]+: ERROR: (.*)', None), (r'intltoolize: \'(.*)\' is out of date: use \'--force\' to overwrite', None), (r"E: pybuild pybuild:[0-9]+: cannot detect build system, please " r"use --system option or set PYBUILD_SYSTEM env\. variable", None), (r'-- Requested \'(.*) >= (.*)\' but version of (.*) is (.*)', lambda m: MissingPkgConfig(m.group(1), minimum_version=m.group(2))), (r'go: go.mod file not found in current directory or any parent directory; ' r'see \'go help modules\'', lambda m: MissingGoModFile()), (r'go: cannot find main module, but found Gopkg.lock in (.*)', lambda m: MissingGoModFile()), (r'go: updates to go.mod needed; to update it:', lambda m: OutdatedGoModFile()), (r'(c\+\+|collect2|cc1|g\+\+): fatal error: .*', None), (r'fatal: making (.*): failed to create tests\/decode.trs', None), # ocaml (r'Please specify at most one of .*', None), # Python lint (r'.*\.py:[0-9]+:[0-9]+: [A-Z][0-9][0-9][0-9] .*', None), (r'PHPUnit requires the \"(.*)\" extension\.', lambda m: MissingPHPExtension(m.group(1))), (r' \[exec\] PHPUnit requires the "(.*)" extension\.', lambda m: MissingPHPExtension(m.group(1))), (r".*/gnulib-tool: \*\*\* minimum supported autoconf version is (.*)\. ", lambda m: MinimumAutoconfTooOld(m.group(1))), (r'# Error: The file "(MANIFEST|META.yml)" is missing from ' 'this distribution\\. .*', lambda m: MissingPerlDistributionFile(m.group(1)), ), (r'\s*> Cannot find \'\.git\' directory', lambda m: VcsControlDirectoryNeeded(['git'])), (r'Unable to find the \'(.*)\' executable\. .*', lambda m: MissingCommand(m.group(1))), (r'\[@RSRCHBOY\/CopyrightYearFromGit\] - ' r'412 No \.git subdirectory found', lambda m: VcsControlDirectoryNeeded(['git'])), (r'Couldn\'t find version control data \(git/hg/bzr/svn supported\)', lambda m: VcsControlDirectoryNeeded(['git', 'hg', 'bzr', 'svn'])), (r'RuntimeError: Unable to determine package version. ' r'No local Git clone detected, and no version file found at .*', lambda m: VcsControlDirectoryNeeded(['git'])), (r'"(.*)" failed to start: "No such file or directory" ' r'at .*.pm line [0-9]+\.', lambda m: MissingCommand(m.group(1))), (r'Can\'t find ([^ ]+)\.', lambda m: MissingCommand(m.group(1))), (r'Error: spawn (.*) ENOENT', lambda m: MissingCommand(m.group(1))), (r'E ImportError: Failed to initialize: Bad (.*) executable\.', lambda m: MissingCommand(m.group(1))), (r'ESLint couldn\'t find the config "(.*)" to extend from\. ' r'Please check that the name of the config is correct\.', None), ( r'E OSError: no library called "cairo-2" was found', lambda m: MissingLibrary(m.group(1)) ), ( r"ERROR: \[Errno 2\] No such file or directory: '(.*)'", file_not_found_maybe_executable, ), ( r"error: \[Errno 2\] No such file or directory: '(.*)'", file_not_found_maybe_executable, ), ( r"ERROR: (.*): commands failed", lambda m: MissingCommand(m.group(1)) ), ( r'We need the Python library (.+) to be installed\. .*', lambda m: MissingPythonDistribution(m.group(1)) ), # ADD NEW REGEXES ABOVE THIS LINE # Intentionally at the bottom of the list. (r'([^ ]+) package not found\. Please install from (https://[^ ]+)', lambda m: MissingVagueDependency(m.group(1), url=m.group(2))), (r'([^ ]+) package not found\. Please use \'pip install .*\' first', lambda m: MissingPythonDistribution(m.group(1))), (r'configure: error: ([^ ]+) development files not found', lambda m: MissingVagueDependency(m.group(1))), (r'Exception: ([^ ]+) development files not found\..*', lambda m: MissingVagueDependency(m.group(1))), (r'Exception: Couldn\'t find (.*) source libs\!', lambda m: MissingVagueDependency(m.group(1))), ('configure: error: \'(.*)\' command was not found', lambda m: MissingCommand(m.group(1))), ( r"configure: error: (.*) not present.*", lambda m: MissingVagueDependency(m.group(1)) ), ( r"configure: error: (.*) >= (.*) not found", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2)) ), ( r"configure: error: (.*) headers not found", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) not found", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) ([0-9.]+) is required to build.*", lambda m: MissingVagueDependency(m.group(1), minimum_version=m.group(2)), ), ( ".*meson.build:([0-9]+):([0-9]+): ERROR: Problem encountered: (.*) (.*) or later required", lambda m: MissingVagueDependency(m.group(3), minimum_version=m.group(4)), ), ( r"configure: error: Please install (.*) from (http:\/\/[^ ]+)", lambda m: MissingVagueDependency(m.group(1), url=m.group(2)), ), ( r"configure: error: Required package (.*) (is ?)not available\.", lambda m: MissingVagueDependency(m.group(1)), ), ( r"Error\! You need to have (.*) \((.*)\) around.", lambda m: MissingVagueDependency(m.group(1), url=m.group(2)), ), ( r"configure: error: You don\'t have (.*) installed", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: Could not find a recent version of (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: Unable to locate (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: Missing the (.* library)", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: (.*) requires (.* libraries), .*", lambda m: MissingVagueDependency(m.group(2)), ), ( r"configure: error: Missing required program '(.*)'.*", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: Missing (.*)\.", lambda m: MissingVagueDependency(m.group(1)), ), ( r"configure: error: Unable to find (.*), please install (.*)", lambda m: MissingVagueDependency(m.group(2)), ), (r"configure: error: (.*) Not found", lambda m: MissingVagueDependency(m.group(1))), ( r"configure: error: You need to install (.*)", lambda m: MissingVagueDependency(m.group(1)), ), ( r'configure: error: (.*) \((.*)\) not found\.', lambda m: MissingVagueDependency(m.group(2)) ), ( r'configure: error: (.*) libraries are required for compilation', lambda m: MissingVagueDependency(m.group(1)) ), ( r'configure: error: .*Make sure you have (.*) installed\.', lambda m: MissingVagueDependency(m.group(1)) ), ( r'error: Cannot find (.*) in the usual places. .*', lambda m: MissingVagueDependency(m.group(1))), ( r'Makefile:[0-9]+: \*\*\* "(.*) was not found"\. Stop\.', lambda m: MissingVagueDependency(m.group(1)) ), (r"([a-z0-9A-Z]+) not found", lambda m: MissingVagueDependency(m.group(1))), (r'ERROR: Unable to locate (.*)\.', lambda m: MissingVagueDependency(m.group(1))), ('\x1b\\[1;31merror: (.*) not found\x1b\\[0;32m', lambda m: MissingVagueDependency(m.group(1))), (r'You do not have (.*) correctly installed\. .*', lambda m: MissingVagueDependency(m.group(1))), (r'Error: (.*) is not available on your system', lambda m: MissingVagueDependency(m.group(1)), ), (r'configure: error: .*Please install the \'(.*)\' package\.', lambda m: MissingVagueDependency(m.group(1))), (r'configure: error: <(.*\.h)> is required', lambda m: MissingCHeader(m.group(1))), (r'configure: error: ([^ ]+) is required', lambda m: MissingVagueDependency(m.group(1))), (r'configure: error: you should install ([^ ]+) first', lambda m: MissingVagueDependency(m.group(1))), (r'configure: error: .*You need (.*) installed.', lambda m: MissingVagueDependency(m.group(1))), (r'To build (.*) you need (.*)', lambda m: MissingVagueDependency(m.group(1))), (r'.*Can\'t ([^\. ]+)\. (.*)', lambda m: MissingVagueDependency(m.group(1))), (r'([^ ]+) >= (.*) is required', lambda m: MissingVagueDependency(m.group(1), m.group(2))), (r'.*: ERROR: (.*) needs to be installed to run these tests', lambda m: MissingVagueDependency(m.group(1))), (r'ERROR: Unable to locate (.*)\.', lambda m: MissingVagueDependency(m.group(1))), (r'ERROR: Cannot find command \'(.*)\' - do you ' r'have \'(.*)\' installed and in your PATH\?', lambda m: MissingCommand(m.group(1))), (r'ValueError: no ([^ ]+) installed, .*', lambda m: MissingVagueDependency(m.group(1))), (r'([^ ]+) executable not found\. .*', lambda m: MissingCommand(m.group(1))), (r'ERROR: InvocationError for command could not find executable (.*)', lambda m: MissingCommand(m.group(1))), (r'E ImportError: Unable to find ([^ ]+) shared library', lambda m: MissingLibrary(m.group(1))), (r'([^ ]+) library not found on the system', lambda m: MissingLibrary(m.group(1))), (r'([^ ]+) library not found\.', lambda m: MissingLibrary(m.group(1))), (r'.*Please install ([^ ]+) libraries\.', lambda m: MissingVagueDependency(m.group(1))), (r'Please get ([^ ]+) from (www\..*)\.', lambda m: MissingVagueDependency(m.group(1), url=m.group(2))), (r'Please install ([^ ]+) so that it is on the PATH and try again\.', lambda m: MissingCommand(m.group(1))), (r'Could not find ([A-Za-z-]+)', lambda m: MissingVagueDependency(m.group(1))), (r'No ([^ ]+) includes and libraries found', lambda m: MissingVagueDependency(m.group(1))), (r'Required library (.*) not found\.', lambda m: MissingVagueDependency(m.group(1))), (r'Missing ([^ ]+) boost library, .*', lambda m: MissingLibrary(m.group(1))), (r'configure: error: ([^ ]+) needed\!', lambda m: MissingVagueDependency(m.group(1))), (r'\*\*\* (.*) not found, please install it \*\*\*', lambda m: MissingVagueDependency(m.group(1))), ( r"configure: error: could not find ([^ ]+)", lambda m: MissingVagueDependency(m.group(1)), ), (r'([^ ]+) is required for ([^ ]+)\.', lambda m: MissingVagueDependency(m.group(1))), (r'configure: error: \*\*\* No ([^.])\! ' r'Install (.*) development headers/libraries! \*\*\*', lambda m: MissingVagueDependency(m.group(1))), (r'configure: error: \'(.*)\' cannot be found', lambda m: MissingVagueDependency(m.group(1))), ] compiled_build_failure_regexps = [] for entry in build_failure_regexps: try: matcher: Matcher if isinstance(entry, tuple): (regexp, cb) = entry matcher = SingleLineMatcher(regexp, cb) else: matcher = entry # type: ignore compiled_build_failure_regexps.append(matcher) except re.error as e: raise Exception("Error in %s: %s" % (regexp, e)) # Regexps that hint at an error of some sort, but not the error itself. secondary_build_failure_regexps = [ r"E: pybuild pybuild:[0-9]+: test: plugin [^ ]+ failed with:", r"[^:]+: error: (.*)", r"[^:]+:[0-9]+: error: (.*)", r"[^:]+:[0-9]+:[0-9]+: error: (.*)", r"error TS[0-9]+: (.*)", r" [0-9]+:[0-9]+\s+error\s+.+", r"fontmake: Error: In '(.*)': (.*)", r"Cannot open file `(.*)' in mode `(.*)' \(No such file or directory\)", r'# Failed test at t\/.*\.t line [0-9]+\.', r'Gradle build daemon disappeared unexpectedly ' r'\(it may have been killed or may have crashed\)', # ocaml r"\*\*\* omake error:", r".*ocamlc.*: OCam has been configured with -force-safe-string: " r"-unsafe-string is not available\.", # latex r"\! LaTeX Error: .*", r"Killed", # Java r'Exception in thread "(.*)" (.*): (.*);', r"error: Unrecognized option: \'.*\'", r".*: No space left on device", r"Segmentation fault", r"\[ERROR\] (.*\.java):\[[0-9]+,[0-9]+\] (.*)", r"make: \*\*\* No targets specified and no makefile found\. Stop\.", r"make\[[0-9]+\]: \*\*\* No targets specified and no makefile found\. Stop\.", r"make: \*\*\* No rule to make target " r"\'(.*)\'\. Stop\.", r"make\[[0-9]+\]: (.*): No such file or directory", r"make\[[0-9]+\]: \*\*\* \[.*:[0-9]+: .*\] Segmentation fault", ( r"make\[[0-9]+\]: \*\*\* No rule to make target " r"\'(?!maintainer-clean)(?!clean)(.*)\'\. Stop\." ), r".*:[0-9]+: \*\*\* empty variable name. Stop.", r"error: can't copy '(.*)': doesn't exist or not a regular file", r"error: ([0-9]+) test executed, ([0-9]+) fatal tests failed, " r"([0-9]+) nonfatal test failed\.", r'.*\.rst:toctree contains ref to nonexisting file \'.*\'', r'.*\.rst:[0-9]+:term not in glossary: .*', r"Try adding AC_PREREQ\(\[(.*)\]\) to your configure\.ac\.", # Erlang r' (.*_test): (.+)\.\.\.\*failed\*', r'(.*\.erl):[0-9]+:[0-9]+: erlang:.*', # Clojure r"Could not locate (.*) or (.*) on classpath\.", # QMake r"Project ERROR: .*", # pdflatex r"\! ==> Fatal error occurred, no output PDF file produced\!", # latex r"\! Undefined control sequence\.", r"\! Emergency stop\.", r"\!pdfTeX error: pdflatex: fwrite\(\) failed", # inkscape r"Unknown option .*", # CTest r'not ok [0-9]+ .*', r"Errors while running CTest", r"dh_auto_install: error: .*", r"dh_quilt_patch: error: (.*)", r"dh.*: Aborting due to earlier error", r"dh.*: unknown option or error during option parsing; aborting", r"Could not import extension .* \(exception: .*\)", r"configure.ac:[0-9]+: error: (.*)", r"Reconfigure the source tree (via './config' or 'perl Configure'), please.", r"dwz: Too few files for multifile optimization", r"\[CJM/MatchManifest\] Aborted because of MANIFEST mismatch", r"dh_dwz: dwz -q -- .* returned exit code [0-9]+", r"help2man: can\'t get `-?-help\' info from .*", r"[^:]+: line [0-9]+:\s+[0-9]+ Segmentation fault.*", r".*(No space left on device).*", r"dpkg-gencontrol: error: (.*)", r".*:[0-9]+:[0-9]+: (error|ERROR): (.*)", r".*[.]+FAILED .*", r"FAIL: (.*)", r"FAIL\! : (.*)", r"\s*FAIL (.*) \(.*\)", r"FAIL\s+(.*) \[.*\] ?", r"([0-9]+)% tests passed, ([0-9]+) tests failed out of ([0-9]+)", r"TEST FAILURE", r"make\[[0-9]+\]: \*\*\* \[.*\] Error [0-9]+", r"make\[[0-9]+\]: \*\*\* \[.*\] Aborted", r"exit code=[0-9]+: .*", r"chmod: cannot access \'.*\': .*", r"dh_autoreconf: autoreconf .* returned exit code [0-9]+", r"make: \*\*\* \[.*\] Error [0-9]+", r".*:[0-9]+: \*\*\* missing separator\. Stop\.", r"[^:]+: cannot stat \'.*\': No such file or directory", r"[0-9]+ tests: [0-9]+ ok, [0-9]+ failure\(s\), [0-9]+ test\(s\) skipped", r"\*\*Error:\*\* (.*)", r"^Error: (.*)", r"Failed [0-9]+ tests? out of [0-9]+, [0-9.]+% okay.", r"Failed [0-9]+\/[0-9]+ test programs. [0-9]+/[0-9]+ subtests failed.", r"Original error was: (.*)", r"-- Error \(.*\.R:[0-9]+:[0-9]+\): \(.*\) [-]*", r"^Error \[ERR_.*\]: .*", r"^FAILED \(.*\)", r"FAILED .*", r"cat: (.*): No such file or directory", # Random Python errors "^(E +)?(SyntaxError|TypeError|ValueError|AttributeError|NameError|" r"django.core.exceptions..*|RuntimeError|subprocess.CalledProcessError|" r"testtools.matchers._impl.MismatchError|" r"PermissionError|IndexError|TypeError|AssertionError|IOError|ImportError|" r"SerialException|OSError|qtawesome.iconic_font.FontError|" "redis.exceptions.ConnectionError|builtins.OverflowError|ArgumentError|" "httptools.parser.errors.HttpParserInvalidURLError|HypothesisException|" "SSLError|KeyError|Exception|rnc2rng.parser.ParseError|" "pkg_resources.UnknownExtra|tarfile.ReadError|" "numpydoc.docscrape.ParseError|distutils.errors.DistutilsOptionError|" "datalad.support.exceptions.IncompleteResultsError|AssertionError|" r"Cython.Compiler.Errors.CompileError|UnicodeDecodeError): .*", # Rust r"error\[E[0-9]+\]: .*", "^E DeprecationWarning: .*", "^E fixture '(.*)' not found", # Rake r"[0-9]+ runs, [0-9]+ assertions, [0-9]+ failures, [0-9]+ errors, " r"[0-9]+ skips", # Node r"# failed [0-9]+ of [0-9]+ tests", # Pytest r"(.*).py:[0-9]+: AssertionError", r"============================ no tests ran in ([0-9.]+)s =============================", # Perl r" Failed tests: [0-9-]+", r"Failed (.*\.t): output changed", # Go r'no packages to test', "FAIL\t(.*)\t[0-9.]+s", r".*.go:[0-9]+:[0-9]+: (?!note:).*", r"can\'t load package: package \.: no Go files in /<>/(.*)", # Ld r"\/usr\/bin\/ld: cannot open output file (.*): No such file or directory", r"configure: error: (.+)", r"config.status: error: (.*)", r"E: Build killed with signal TERM after ([0-9]+) minutes of inactivity", r" \[javac\] [^: ]+:[0-9]+: error: (.*)", r"1\) TestChannelFeature: ([^:]+):([0-9]+): assert failed", r"cp: target \'(.*)\' is not a directory", r"cp: cannot create regular file \'(.*)\': No such file or directory", r"couldn\'t determine home directory at (.*)", r"ln: failed to create symbolic link \'(.*)\': File exists", r"ln: failed to create symbolic link \'(.*)\': No such file or directory", r"ln: failed to create symbolic link \'(.*)\': Permission denied", r"ln: invalid option -- .*", r"mkdir: cannot create directory [‘'](.*)['’]: No such file or directory", r"mkdir: cannot create directory [‘'](.*)['’]: File exists", r"mkdir: missing operand", r"rmdir: failed to remove '.*': No such file or directory", r"Fatal error: .*", "Fatal Error: (.*)", r"Alert: (.*)", r'ERROR: Test "(.*)" failed. Exiting.', # scons r"ERROR: test\(s\) failed in (.*)", r"./configure: line [0-9]+: syntax error near unexpected token `.*\'", r"scons: \*\*\* \[.*\] ValueError : unsupported pickle protocol: .*", # yarn r"ERROR: There are no scenarios; must have at least one.", # perl r"Execution of (.*) aborted due to compilation errors.", r"ls: cannot access \'(.*)\': No such file or directory", r"Problem opening (.*): No such file or directory at (.*) line ([0-9]+)\.", # Mocha r" AssertionError \[ERR_ASSERTION\]: Missing expected exception.", # lt (C++) r".*: .*:[0-9]+: .*: Assertion `.*\' failed.", r"(.*).xml: FAILED:", r" BROKEN .*", r'failed: [0-9]+-.*', # ninja r"ninja: build stopped: subcommand failed.", r".*\.s:[0-9]+: Error: .*", # rollup r"\[\!\] Error: Unexpected token", # glib r"\(.*:[0-9]+\): [a-zA-Z0-9]+-CRITICAL \*\*: [0-9:.]+: .*", r"tar: option requires an argument -- \'.\'", r"tar: .*: Cannot stat: No such file or directory", r"tar: .*: Cannot open: No such file or directory", # rsvg-convert r"Could not render file (.*.svg)", # pybuild tests r"ERROR: file not found: (.*)", # msgfmt r"/usr/bin/msgfmt: found [0-9]+ fatal errors", # Docker r"Cannot connect to the Docker daemon at " r"unix:///var/run/docker.sock. Is the docker daemon running\?", r"dh_makeshlibs: failing due to earlier errors", # Ruby r"([^:]+)\.rb:[0-9]+:in `([^\'])+\': (.*) \((.*)\)", r".*: \*\*\* ERROR: " r"There where errors/warnings in server logs after running test cases.", r"Errno::EEXIST: File exists @ dir_s_mkdir - .*", r"Test environment was found to be incomplete at configuration time,", r"libtool: error: cannot find the library \'(.*)\' or " r"unhandled argument \'(.*)\'", r"npm ERR\! (.*)", r"install: failed to access \'(.*)\': (.*)", r"MSBUILD: error MSBUILD[0-9]+: Project file \'(.*)\' not found.", r"E: (.*)", r"(.*)\(([0-9]+),([0-9]+)\): Error: .*", # C # r"(.*)\.cs\([0-9]+,[0-9]+\): error CS[0-9]+: .*", r".*Segmentation fault.*", r"a2x: ERROR: (.*) returned non-zero exit status ([0-9]+)", r"-- Configuring incomplete, errors occurred\!", r'Error opening link script "(.*)"', r"cc: error: (.*)", r"\[ERROR\] .*", r"dh_auto_(test|build): error: (.*)", r"tar: This does not look like a tar archive", r"\[DZ\] no (name|version) was ever set", r"\[Runtime\] No -phase or -relationship specified at .* line [0-9]+\.", r"diff: (.*): No such file or directory", r"gpg: signing failed: .*", # mh_install r"Cannot find the jar to install: (.*)", r"ERROR: .*", r"> error: (.*)", ] compiled_secondary_build_failure_regexps = [] for regexp in secondary_build_failure_regexps: try: compiled_secondary_build_failure_regexps.append(re.compile(regexp)) except re.error as e: raise Exception("Error compiling %r: %s" % (regexp, e)) def find_build_failure_description( # noqa: C901 lines: List[str], ) -> Tuple[Optional[Match], Optional["Problem"]]: """Find the key failure line in build output. Returns: tuple with (match object, error object) """ OFFSET = 250 # Is this cmake-specific, or rather just kf5 / qmake ? cmake = False # We search backwards for clear errors. for i in range(1, OFFSET): lineno = len(lines) - i if lineno < 0: break if "cmake" in lines[lineno]: cmake = True for matcher in compiled_build_failure_regexps: linenos, err = matcher.match(lines, lineno) if linenos: return MultiLineMatch.from_lines(lines, linenos), err # TODO(jelmer): Remove this in favour of CMakeErrorMatcher above. if cmake: missing_file_pat = re.compile( r"\s*The imported target \"(.*)\" references the file" ) binary_pat = re.compile(r" Could NOT find (.*) \(missing: .*\)") cmake_files_pat = re.compile( "^ Could not find a package configuration file provided " 'by "(.*)" with any of the following names:' ) # Urgh, multi-line regexes--- for lineno in range(len(lines)): line = lines[lineno].rstrip("\n") m = re.fullmatch(binary_pat, line) if m: return ( SingleLineMatch.from_lines(lines, lineno), MissingCommand(m.group(1).lower()), ) m = re.fullmatch(missing_file_pat, line) if m: lineno += 1 while lineno < len(lines) and not line: lineno += 1 if lines[lineno + 2].startswith(" but this file does not exist."): m = re.fullmatch(r'\s*"(.*)"', line) if m: filename = m.group(1) else: filename = line return ( SingleLineMatch.from_lines(lines, lineno), MissingFile(filename), ) continue if lineno + 1 < len(lines): m = re.fullmatch( cmake_files_pat, line + " " + lines[lineno + 1].lstrip(" ").strip("\n"), ) if m and lines[lineno + 2] == "\n": i = 3 filenames = [] while lines[lineno + i].strip(): filenames.append(lines[lineno + i].strip()) i += 1 return ( SingleLineMatch.from_lines(lines, lineno), CMakeFilesMissing(filenames), ) # And forwards for vague ("secondary") errors. for lineno in range(max(0, len(lines) - OFFSET), len(lines)): line = lines[lineno].strip("\n") for regexp in compiled_secondary_build_failure_regexps: m = regexp.fullmatch(line) if m: return SingleLineMatch.from_lines(lines, lineno), None return None, None def main(argv=None): import argparse import json parser = argparse.ArgumentParser("analyse-build-log") parser.add_argument("path", type=str) parser.add_argument("--context", "-c", type=int, default=5) parser.add_argument("--json", action="store_true", help="Output JSON.") parser.add_argument( "--version", action="version", version="%(prog)s " + version_string ) args = parser.parse_args(argv) logging.basicConfig(level=logging.INFO, format="%(message)s") with open(args.path, "r") as f: lines = list(f.readlines()) m, problem = find_build_failure_description(lines) if args.json: ret = {} if m: ret["lineno"] = m.lineno ret["line"] = m.line if problem: ret["problem"] = problem.kind try: ret["details"] = problem.json() except NotImplementedError: ret["details"] = None json.dump(ret, sys.stdout, indent=4) else: if not m: logging.info("No issues found") else: if len(m.linenos) == 1: logging.info("Issue found at line %d:", m.lineno) else: logging.info( "Issue found at lines %d-%d:", m.linenos[0], m.linenos[-1]) for i in range( max(0, m.offsets[0] - args.context), min(len(lines), m.offsets[-1] + args.context + 1), ): logging.info( " %s %s", ">" if i in m.offsets else " ", lines[i].rstrip("\n") ) if problem: logging.info("Identified issue: %s: %s", problem.kind, problem) if __name__ == "__main__": import sys sys.exit(main(sys.argv[1:])) buildlog-consultant_0.0.18.orig/buildlog_consultant/sbuild.py0000644000000000000000000010150414162106315021447 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # encoding: utf-8 # # 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 import re from typing import List, Tuple, Iterator, BinaryIO, Optional, Union from dataclasses import dataclass import logging from . import Problem, problem, SingleLineMatch, version_string from .apt import ( find_apt_get_failure, find_apt_get_update_failure, find_install_deps_failure_description, ) from .autopkgtest import find_autopkgtest_failure_description from .common import find_build_failure_description, NoSpaceOnDevice, ChrootNotFound, PatchApplicationFailed __all__ = [ "SbuildFailure", "parse_sbuild_log", "SbuildLog", ] logger = logging.getLogger(__name__) class SbuildFailure(Exception): """Sbuild failed to run.""" def __init__( self, stage: Optional[str], description: Optional[str], error: Optional["Problem"] = None, phase: Optional[Union[Tuple[str], Tuple[str, Optional[str]]]] = None, section: Optional["SbuildLogSection"] = None, match: Optional[SingleLineMatch] = None, ): self.stage = stage self.description = description self.error = error self.phase = phase self.section = section self.match = match def __repr__(self): return "%s(%r, %r, error=%r, phase=%r)" % ( type(self).__name__, self.stage, self.description, self.error, self.phase, ) def json(self): ret = { "stage": self.stage, "phase": self.phase, "section": self.section.title if self.section else None, "lineno": (self.section.offsets[0] + self.match.lineno) if self.match else None, } if self.error: ret["kind"] = self.error.kind try: ret["details"] = self.error.json() except NotImplementedError: ret["details"] = None return ret @problem("unexpected-local-upstream-changes") class DpkgSourceLocalChanges: files: Optional[List[str]] = None def __repr__(self): if len(self.files) < 5: return "%s(%r)" % (type(self).__name__, self.files) else: return "<%s(%d files)>" % (type(self).__name__, len(self.files)) def __str__(self): if self.files and len(self.files) < 5: return "Tree has local changes: %r" % self.files elif self.files: return "Tree has local changes: %d files" % len(self.files) else: return "Tree has local changes" @problem("unrepresentable-local-changes") class DpkgSourceUnrepresentableChanges: def __str__(self): return "Tree has unrepresentable local changes." @problem("unwanted-binary-files") class DpkgUnwantedBinaryFiles: def __str__(self): return "Tree has unwanted binary files." @problem("changed-binary-files") class DpkgBinaryFileChanged: paths: List[str] def __str__(self): return "Tree has binary files with changes: %r" % self.paths @problem("missing-control-file") class MissingControlFile: path: str def __str__(self): return "Tree is missing control file %s" % self.path @problem("unable-to-find-upstream-tarball") class UnableToFindUpstreamTarball: package: str version: str def __str__(self): return "Unable to find the needed upstream tarball for " "%s, version %s." % ( self.package, self.version, ) @problem("source-format-unbuildable") class SourceFormatUnbuildable: source_format: str reason: str def __str__(self): return "Source format %s unusable: %s" % ( self.source_format, self.reason) @problem("unsupported-source-format") class SourceFormatUnsupported: source_format: str def __str__(self): return "Source format %r unsupported" % self.source_format @problem("patch-file-missing") class PatchFileMissing: path: str def __str__(self): return "Patch file %s missing" % self.path @problem("unknown-mercurial-extra-fields") class UnknownMercurialExtraFields: field: str def __str__(self): return "Unknown Mercurial extra fields: %s" % self.field @problem("upstream-pgp-signature-verification-failed") class UpstreamPGPSignatureVerificationFailed: def __str__(self): return "Unable to verify the PGP signature on the upstream source" @problem("uscan-requested-version-missing") class UScanRequestVersionMissing: version: str def __str__(self): return "UScan can not find requested version %s." % self.version @problem("debcargo-failed") class DebcargoFailure: reason: str def __str__(self): if self.reason: return "Debcargo failed: %s" % self.reason else: return "Debcargo failed" @problem("changelog-parse-failed") class ChangelogParseError: reason: str def __str__(self): return "Changelog failed to parse: %s" % self.reason @problem("uscan-failed") class UScanFailed: url: str reason: str def __str__(self): return "UScan failed to download %s: %s." % (self.url, self.reason) @problem("inconsistent-source-format") class InconsistentSourceFormat: def __str__(self): return "Inconsistent source format between version and source format" @problem("debian-upstream-metadata-invalid") class UpstreamMetadataFileParseError: path: str reason: str def __str__(self): return "%s is invalid" % self.path @problem("dpkg-source-pack-failed") class DpkgSourcePackFailed: reason: Optional[str] = None def __str__(self): if self.reason: return "Packing source directory failed: %s" % self.reason else: return "Packing source directory failed." @problem("dpkg-bad-version") class DpkgBadVersion: version: str reason: Optional[str] = None def __str__(self): if self.reason: return "Version (%s) is invalid: %s" % (self.version, self.reason) else: return "Version (%s) is invalid" % self.version @problem("debcargo-missing-crate") class MissingDebcargoCrate: crate: str version: Optional[str] = None @classmethod def from_string(cls, text): text = text.strip() if "=" in text: (crate, version) = text.split("=") return cls(crate.strip(), version.strip()) else: return cls(text) def __str__(self): ret = "debcargo can't find crate %s" % self.crate if self.version: ret += " (version: %s)" % self.version return ret def find_preamble_failure_description( # noqa: C901 lines: List[str], ) -> Tuple[Optional[SingleLineMatch], Optional[Problem]]: ret: Tuple[Optional[int], Optional[str], Optional[Problem]] = (None, None) OFFSET = 100 err: Problem for i in range(1, OFFSET): lineno = len(lines) - i if lineno < 0: break line = lines[lineno].strip("\n") if line.startswith( "dpkg-source: error: aborting due to unexpected upstream " "changes, see " ): j = lineno - 1 files: List[str] = [] while j > 0: if lines[j] == ( "dpkg-source: info: local changes detected, " "the modified files are:\n" ): err = DpkgSourceLocalChanges(files) return SingleLineMatch.from_lines(lines, lineno), err files.append(lines[j].strip()) j -= 1 err = DpkgSourceLocalChanges() return SingleLineMatch.from_lines(lines, lineno), err if line == "dpkg-source: error: unrepresentable changes to source": err = DpkgSourceUnrepresentableChanges() return SingleLineMatch.from_lines(lines, lineno), err if re.match( "dpkg-source: error: detected ([0-9]+) unwanted binary " "file.*", line ): err = DpkgUnwantedBinaryFiles() return SingleLineMatch.from_lines(lines, lineno), err m = re.match( "dpkg-source: error: cannot read (.*/debian/control): " "No such file or directory", line, ) if m: err = MissingControlFile(m.group(1)) return SingleLineMatch.from_lines(lines, lineno), err m = re.match("dpkg-source: error: .*: No space left on device", line) if m: err = NoSpaceOnDevice() return SingleLineMatch.from_lines(lines, lineno), err m = re.match("tar: .*: Cannot write: No space left on device", line) if m: err = NoSpaceOnDevice() return SingleLineMatch.from_lines(lines, lineno), err m = re.match( "dpkg-source: error: cannot represent change to (.*): " "binary file contents changed", line, ) if m: err = DpkgBinaryFileChanged([m.group(1)]) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( r"dpkg-source: error: source package format \'(.*)\' is not " r"supported: Can\'t locate (.*) in \@INC " r"\(you may need to install the (.*) module\) " r"\(\@INC contains: (.*)\) at \(eval [0-9]+\) line [0-9]+\.", line, ) if m: err = SourceFormatUnsupported(m.group(1)) return SingleLineMatch.from_lines(lines, lineno), err m = re.match("E: Failed to package source directory (.*)", line) if m: err = DpkgSourcePackFailed() ret = SingleLineMatch.from_lines(lines, lineno), err m = re.match("E: Bad version unknown in (.*)", line) if m and lines[lineno - 1].startswith("LINE: "): m = re.match( r"dpkg-parsechangelog: warning: .*\(l[0-9]+\): " r"version \'(.*)\' is invalid: (.*)", lines[lineno - 2], ) if m: err = DpkgBadVersion(m.group(1), m.group(2)) return SingleLineMatch.from_lines(lines, lineno), err m = re.match("Patch (.*) does not apply \\(enforce with -f\\)\n", line) if m: patchname = m.group(1).split("/")[-1] err = PatchApplicationFailed(patchname) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( r"dpkg-source: error: LC_ALL=C patch .* " r"--reject-file=- < .*\/debian\/patches\/([^ ]+) " r"subprocess returned exit status 1", line, ) if m: patchname = m.group(1) err = PatchApplicationFailed(patchname) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( "dpkg-source: error: " "can't build with source format '(.*)': " "(.*)", line, ) if m: err = SourceFormatUnbuildable(m.group(1), m.group(2)) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( "dpkg-source: error: cannot read (.*): " "No such file or directory", line, ) if m: err = PatchFileMissing(m.group(1).split("/", 1)[1]) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( "dpkg-source: error: " "source package format '(.*)' is not supported: " "(.*)", line, ) if m: (match, err) = find_build_failure_description([m.group(2)]) if err is None: err = SourceFormatUnsupported(m.group(1)) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( "breezy.errors.NoSuchRevision: " "(.*) has no revision b'(.*)'", line, ) if m: err = MissingRevision(m.group(2).encode()) return SingleLineMatch.from_lines(lines, lineno), err m = re.match( r'fatal: ambiguous argument \'(.*)\': ' r'unknown revision or path not in the working tree.', line) if m: err = PristineTarTreeMissing(m.group(1)) return SingleLineMatch.from_lines(lines, lineno), err m = re.match("dpkg-source: error: (.*)", line) if m: err = DpkgSourcePackFailed(m.group(1)) ret = SingleLineMatch.from_lines(lines, lineno), err return ret @problem("debcargo-unacceptable-predicate") class DebcargoUnacceptablePredicate: predicate: str def __str__(self): return "Cannot represent prerelease part of dependency: %s" % (self.predicate) def _parse_debcargo_failure(m, pl): MORE_TAIL = "\x1b[0m\n" MORE_HEAD = "\x1b[1;31mSomething failed: " if pl[-1].endswith(MORE_TAIL): extra = [pl[-1][: -len(MORE_TAIL)]] for line in reversed(pl[:-1]): if extra[0].startswith(MORE_HEAD): extra[0] = extra[0][len(MORE_HEAD) :] break extra.insert(0, line) else: extra = [] if extra and extra[-1] == ( " Try `debcargo update` to update the crates.io index." ): n = re.match(r"Couldn\'t find any crate matching (.*)", extra[-2]) if n: return MissingDebcargoCrate.from_string(n.group(1)) else: return DpkgSourcePackFailed(extra[-2]) elif extra: m = re.match( r"Cannot represent prerelease part of dependency: (.*) Predicate \{ (.*) \}", extra[0], ) if m: return DebcargoUnacceptablePredicate(m.group(2)) else: return DebcargoFailure("".join(extra)) return DebcargoFailure("Debcargo failed to run") @problem("uscan-too-many-requests") class UScanTooManyRequests: url: str def __str__(self): return "UScan: %s: too many requests" % self.url BRZ_ERRORS = [ ( "Unable to find the needed upstream tarball for " "package (.*), version (.*)\\.", lambda m, pl: UnableToFindUpstreamTarball(m.group(1), m.group(2)), ), ( "Unknown mercurial extra fields in (.*): b'(.*)'.", lambda m, pl: UnknownMercurialExtraFields(m.group(2)), ), ( r"UScan failed to run: In watchfile (.*), reading webpage " r"(.*) failed: 429 too many requests\.", lambda m, pl: UScanTooManyRequests(m.group(2)) ), ( "UScan failed to run: OpenPGP signature did not verify..", lambda m, pl: UpstreamPGPSignatureVerificationFailed(), ), ( r"Inconsistency between source format and version: " r"version is( not)? native, format is( not)? native\.", lambda m, pl: InconsistentSourceFormat(), ), ( r"UScan failed to run: In (.*) no matching hrefs " "for version (.*) in watch line", lambda m, pl: UScanRequestVersionMissing(m.group(2)), ), ( r"UScan failed to run: In directory ., downloading \s+" r"(.*) failed: (.*)", lambda m, pl: UScanFailed(m.group(1), m.group(2)), ), ( r"UScan failed to run: In watchfile debian/watch, " r"reading webpage\n (.*) failed: (.*)", lambda m, pl: UScanFailed(m.group(1), m.group(2)), ), ( r"Unable to parse upstream metadata file (.*): (.*)", lambda m, pl: UpstreamMetadataFileParseError(m.group(1), m.group(2)), ), (r"Debcargo failed to run\.", _parse_debcargo_failure), ( r"\[Errno 28\] No space left on device", lambda m, pl: NoSpaceOnDevice(), ), ] _BRZ_ERRORS = [(re.compile(r), fn) for (r, fn) in BRZ_ERRORS] def parse_brz_error(line: str, prior_lines: List[str]) -> Tuple[Optional[Problem], str]: error: Problem line = line.strip() for search_re, fn in _BRZ_ERRORS: m = search_re.match(line) if m: error = fn(m, prior_lines) return (error, str(error)) if line.startswith("UScan failed to run"): return (UScanFailed(None, line[len("UScan failed to run: "):]), line) if line.startswith('Unable to parse changelog: '): return (ChangelogParseError(line[len("Unable to parse changelog: "):]), line) return (None, line.split("\n")[0]) @problem("missing-revision") class MissingRevision: revision: bytes def __str__(self): return "Missing revision: %r" % self.revision @problem("pristine-tar-missing-tree") class PristineTarTreeMissing: treeish: str def __str__(self): return "pristine-tar can not find tree %r" % self.treeish def find_creation_session_error(lines): ret = None, None for i in range(len(lines) - 1, 0, -1): line = lines[i] if line.startswith("E: "): ret = SingleLineMatch.from_lines(lines, i), None m = re.fullmatch( "E: Chroot for distribution (.*), architecture (.*) not found\n", line ) if m: return SingleLineMatch.from_lines(lines, i), ChrootNotFound( "%s-%s-sbuild" % (m.group(1), m.group(2)) ) if line.endswith(": No space left on device\n"): return SingleLineMatch.from_lines(lines, i), NoSpaceOnDevice() return ret def find_brz_build_error(lines): for i in range(len(lines) - 1, 0, -1): line = lines[i] if line.startswith("brz: ERROR: "): rest = [line[len("brz: ERROR: ") :]] for n in lines[i + 1 :]: if n.startswith(" "): rest.append(n) return parse_brz_error("".join(rest), lines[:i]) return (None, None) @dataclass class SbuildLogSection: title: Optional[str] offsets: Tuple[int, int] lines: List[str] @dataclass class SbuildLog(object): sections: List[SbuildLogSection] def get_section(self, title): for section in self.sections: if section.title is None and title is None: return section if section.title and title and section.title.lower() == title.lower(): return section def get_section_lines(self, title): section = self.get_section(title) if section: return section.lines return [] def section_titles(self): return [section.title for section in self.sections] @classmethod def parse(cls, f: BinaryIO): sections = [] for section in parse_sbuild_log(f): logging.debug( "Section %s (lines %d-%d)" % (section.title, section.offsets[0], section.offsets[1]) ) sections.append(section) return cls(sections) def get_failed_stage(self) -> Optional[str]: return find_failed_stage(self.get_section_lines("summary")) def find_failure_fetch_src(sbuildlog, failed_stage): section = sbuildlog.get_section("fetch source files") if not section: logging.warning("expected section: fetch source files") return None section_lines = section.lines if not section_lines[0].strip(): section_lines = section_lines[1:] if len(section_lines) == 1 and section_lines[0].startswith("E: Could not find "): match, error = find_preamble_failure_description( sbuildlog.get_section_lines(None) ) return SbuildFailure("unpack", str(error), error, section=section, match=match) (match, error) = find_apt_get_failure(section.lines) description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=None, section=section, match=match ) def find_failure_create_session(sbuildlog, failed_stage): section = sbuildlog.get_section(None) match, error = find_creation_session_error(section.lines) phase = ("create-session",) description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=phase, section=section, match=match, ) def find_failure_unpack(sbuildlog, failed_stage): section = sbuildlog.get_section("build") match, error = find_preamble_failure_description(section.lines) if error: return SbuildFailure( failed_stage, str(error), error, section=section, match=match ) description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=None, section=section, match=match ) def find_failure_build(sbuildlog, failed_stage): section = sbuildlog.get_section("build") phase = ("build",) section_lines, files = strip_build_tail(section.lines) match, error = find_build_failure_description(section_lines) if error: description = str(error) elif match: description = match.line.rstrip("\n") else: description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=phase, section=section, match=match, ) def find_failure_autopkgtest(sbuildlog, failed_stage): focus_section = { "run-post-build-commands": "post build commands", "post-build": "post build", "autopkgtest": "autopkgtest", }[failed_stage] section = sbuildlog.get_section(focus_section) if section is not None: ( match, testname, error, description, ) = find_autopkgtest_failure_description(section.lines) if not description: description = str(error) phase = ("autopkgtest", testname) else: description = None error = None match = None phase = None if not description: description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=phase, section=section, match=match, ) def find_failure_apt_get_update(sbuildlog, failed_stage): focus_section, match, error = find_apt_get_update_failure(sbuildlog) if error: description = str(error) elif match: description = match.line.rstrip("\n") else: description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=None, section=sbuildlog.get_section(focus_section), match=match, ) def find_failure_arch_check(sbuildlog, failed_stage): section = sbuildlog.get_section( "check architectures", ) (match, error) = find_arch_check_failure_description(section.lines) if error: description = str(error) else: description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=None, section=section, match=match ) def find_failure_check_space(sbuildlog, failed_stage): section = sbuildlog.get_section("cleanup") (match, error) = find_check_space_failure_description(section.lines) if error: description = str(error) else: description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=error, phase=None, section=section, match=match ) def find_failure_install_deps(sbuildlog, failed_stage): (focus_section, match, error) = find_install_deps_failure_description(sbuildlog) if error: description = str(error) elif match: if match.line.startswith("E: "): description = match.line[3:].rstrip("\n") else: description = match.line.rstrip("\n") else: description = "build failed stage %s" % failed_stage phase = ("build",) return SbuildFailure( failed_stage, description, error=error, phase=phase, section=sbuildlog.get_section(focus_section), match=match, ) FAILED_STAGE_FAIL_FINDERS = { "fetch-src": find_failure_fetch_src, "create-session": find_failure_create_session, "unpack": find_failure_unpack, "build": find_failure_build, "apt-get-update": find_failure_apt_get_update, "arch-check": find_failure_arch_check, "check-space": find_failure_check_space, "install-deps": find_failure_install_deps, "explain-bd-uninstallable": find_failure_install_deps, "autopkgtest": find_failure_autopkgtest, # We run autopkgtest as only post-build step at the moment. "run-post-build-commands": find_failure_autopkgtest, "post-build": find_failure_autopkgtest, } def worker_failure_from_sbuild_log(f: Union[SbuildLog, BinaryIO]) -> SbuildFailure: # noqa: C901 if isinstance(f, SbuildLog): sbuildlog = f else: sbuildlog = SbuildLog.parse(f) # TODO(jelmer): Doesn't this do the same thing as the tail? if len(sbuildlog.sections) == 1: match, error = find_preamble_failure_description(sbuildlog.sections[0].lines) if error: return SbuildFailure( "unpack", str(error), error, section=sbuildlog.sections[0], match=match ) failed_stage = sbuildlog.get_failed_stage() try: overall_failure = FAILED_STAGE_FAIL_FINDERS[failed_stage]( sbuildlog, failed_stage ) except KeyError: if failed_stage is not None: logging.warning("unknown failed stage: %s", failed_stage) description = "build failed stage %s" % failed_stage return SbuildFailure( failed_stage, description, error=None, phase=None, section=None, match=None ) else: if overall_failure is not None: return overall_failure description = "build failed" phase = ("buildenv",) if sbuildlog.section_titles() == [None]: section = sbuildlog.sections[0] match, error = find_preamble_failure_description(section.lines) if error is not None: description = str(error) else: (match, error) = find_build_failure_description(section.lines) if match is None: error, description = find_brz_build_error(section.lines) else: description = match.line.rstrip("\n") return SbuildFailure( failed_stage, description, error=error, phase=phase, section=section, match=match, ) return SbuildFailure( failed_stage, description, error=None, phase=phase, section=None, match=None, ) def parse_sbuild_log(f: BinaryIO) -> Iterator[SbuildLogSection]: begin_offset = 1 lines: List[str] = [] title = None sep = b"+" + (b"-" * 78) + b"+" lineno = 0 line = f.readline() lineno += 1 while line: if line.strip() == sep: l1 = f.readline() l2 = f.readline() lineno += 2 if l1.startswith(b"|") and l1.strip().endswith(b"|") and l2.strip() == sep: end_offset = lineno - 3 # Drop trailing empty lines while lines and lines[-1] == "\n": lines.pop(-1) end_offset -= 1 if lines: yield SbuildLogSection(title, (begin_offset, end_offset), lines) title = l1.rstrip()[1:-1].strip().decode(errors="replace") lines = [] begin_offset = lineno else: lines.extend( [ line.decode(errors="replace"), l1.decode(errors="replace"), l2.decode(errors="replace"), ] ) else: lines.append(line.decode(errors="replace")) line = f.readline() lineno += 1 yield SbuildLogSection(title, (begin_offset, lineno), lines) def find_failed_stage(lines: List[str]) -> Optional[str]: for line in lines: if not line.startswith("Fail-Stage: "): continue (key, value) = line.split(": ", 1) return value.strip() return None DEFAULT_LOOK_BACK = 50 def strip_build_tail(lines, look_back=None): if look_back is None: look_back = DEFAULT_LOOK_BACK # Strip off unuseful tail for i, line in enumerate(lines[-look_back:]): if line.startswith("Build finished at "): lines = lines[: len(lines) - (look_back - i)] if lines and lines[-1] == ("-" * 80 + "\n"): lines = lines[:-1] break files = {} current_contents = [] header_re = re.compile(r"==\> (.*) \<==\n") for i in range(len(lines) - 1, -1, -1): m = header_re.match(lines[i]) if m: files[m.group(1)] = current_contents current_contents = [] lines = lines[:i] continue return lines, files @problem("arch-not-in-list") class ArchitectureNotInList: arch: str arch_list: List[str] def __str__(self): return "Architecture %s not a build arch" % (self.arch,) def find_arch_check_failure_description( lines: List[str], ) -> Tuple[SingleLineMatch, Optional[Problem]]: for offset, line in enumerate(lines): m = re.match( r"E: dsc: (.*) not in arch list or does not match any arch " r"wildcards: (.*) -- skipping", line, ) if m: error = ArchitectureNotInList(m.group(1), m.group(2)) return SingleLineMatch.from_lines(lines, offset), error return SingleLineMatch.from_lines(lines, len(lines) - 1), None @problem("insufficient-disk-space") class InsufficientDiskSpace: needed: int free: int def __str__(self): return "Insufficient disk space for build. " "Need: %d KiB, free: %s KiB" % ( self.needed, self.free, ) def find_check_space_failure_description( lines, ) -> Tuple[SingleLineMatch, Optional[Problem]]: for offset, line in enumerate(lines): if line == "E: Disk space is probably not sufficient for building.\n": m = re.fullmatch( r"I: Source needs ([0-9]+) KiB, " r"while ([0-9]+) KiB is free.\)\n", lines[offset + 1], ) if m: return ( SingleLineMatch.from_lines(lines, offset), InsufficientDiskSpace(int(m.group(1)), int(m.group(2))), ) return SingleLineMatch.from_lines(lines, offset), None def main(argv=None): import argparse import json parser = argparse.ArgumentParser("analyse-sbuild-log") parser.add_argument("--debug", action="store_true", help="Display debug output.") parser.add_argument("--json", action="store_true", help="Output JSON.") parser.add_argument( "--context", "-c", type=int, default=5, help="Number of context lines to print." ) parser.add_argument( "--version", action="version", version="%(prog)s " + version_string ) parser.add_argument("path", type=str) args = parser.parse_args() if args.debug: loglevel = logging.DEBUG elif args.json: loglevel = logging.WARNING else: loglevel = logging.INFO logging.basicConfig(level=loglevel, format="%(message)s") with open(args.path, "rb") as f: sbuildlog = SbuildLog.parse(f) failed_stage = sbuildlog.get_failed_stage() if failed_stage: logging.info("Failed stage: %s" % failed_stage) failure = worker_failure_from_sbuild_log(sbuildlog) if args.json: json.dump(failure.json(), sys.stdout, indent=4) if failure.error: logging.info("Error: %s" % failure.error) if failure.match: logging.info( "Failed line: %d:" % (failure.section.offsets[0] + failure.match.lineno) ) for i in range( max(0, failure.match.offset - args.context), min(len(failure.section.lines), failure.match.offset + args.context + 1), ): logging.info( " %s %s", ">" if failure.match.offset == i else " ", failure.section.lines[i].rstrip("\n"), ) if __name__ == "__main__": import sys sys.exit(main(sys.argv)) buildlog-consultant_0.0.18.orig/buildlog_consultant/tests/0000755000000000000000000000000014006376416020764 5ustar00buildlog-consultant_0.0.18.orig/buildlog_consultant/tests/__init__.py0000644000000000000000000000211614016462165023073 0ustar00#!/usr/bin/python # Copyright (C) 2019 Jelmer Vernooij # # 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 from __future__ import absolute_import import unittest def test_suite(): names = [ "apt", "autopkgtest", "common", "sbuild", ] module_names = [__name__ + ".test_" + name for name in names] loader = unittest.TestLoader() return loader.loadTestsFromNames(module_names) buildlog-consultant_0.0.18.orig/buildlog_consultant/tests/test_apt.py0000644000000000000000000000435414205553356023170 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # # 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 import unittest from ..apt import ( AptFetchFailure, AptMissingReleaseFile, find_apt_get_failure, ) class FindAptGetFailureDescriptionTests(unittest.TestCase): def run_test(self, lines, lineno, err=None): (match, actual_err) = find_apt_get_failure(lines) if lineno is not None: self.assertEqual(match.line, lines[lineno - 1]) self.assertEqual(match.lineno, lineno) else: self.assertIsNone(match) if err: self.assertEqual(actual_err, err) else: self.assertIs(None, actual_err) def test_make_missing_rule(self): self.run_test( [ """\ E: Failed to fetch http://janitor.debian.net/blah/Packages.xz \ File has unexpected size (3385796 != 3385720). Mirror sync in progress? [IP]\ """ ], 1, AptFetchFailure( "http://janitor.debian.net/blah/Packages.xz", "File has unexpected size (3385796 != 3385720). " "Mirror sync in progress? [IP]", ), ) def test_missing_release_file(self): self.run_test( [ """\ E: The repository 'https://janitor.debian.net blah/ Release' \ does not have a Release file.\ """ ], 1, AptMissingReleaseFile("http://janitor.debian.net/ blah/ Release"), ) def test_vague(self): self.run_test(["E: Stuff is broken"], 1, None) buildlog-consultant_0.0.18.orig/buildlog_consultant/tests/test_autopkgtest.py0000644000000000000000000005045514034351606024753 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # # 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 import unittest from ..autopkgtest import ( AutopkgtestTestbedFailure, AutopkgtestDepsUnsatisfiable, AutopkgtestDepChrootDisappeared, AutopkgtestTimedOut, AutopkgtestStderrFailure, find_autopkgtest_failure_description, ) from .. import SingleLineMatch from ..common import MissingCommand, MissingFile class FindAutopkgtestFailureDescriptionTests(unittest.TestCase): def test_empty(self): self.assertEqual( (None, None, None, None), find_autopkgtest_failure_description([]) ) def test_no_match(self): self.assertEqual( (SingleLineMatch(0, "blalblala\n"), "blalblala\n", None, None), find_autopkgtest_failure_description(["blalblala\n"]), ) def test_unknown_error(self): self.assertEqual( ( SingleLineMatch(1, "python-bcolz FAIL some error\n"), "python-bcolz", None, "Test python-bcolz failed: some error", ), find_autopkgtest_failure_description( [ "autopkgtest [07:58:03]: @@@@@@@@@@@@@@@@@@@@ summary\n", "python-bcolz FAIL some error\n", ] ), ) def test_timed_out(self): error = AutopkgtestTimedOut() self.assertEqual( ( SingleLineMatch(1, "unit-tests FAIL timed out"), "unit-tests", error, "timed out", ), find_autopkgtest_failure_description( [ "autopkgtest [07:58:03]: @@@@@@@@@@@@@@@@@@@@ summary\n", "unit-tests FAIL timed out", ] ), ) def test_deps(self): error = AutopkgtestDepsUnsatisfiable( [ ( "arg", "/home/janitor/tmp/tmppvupofwl/build-area/" "bcolz-doc_1.2.1+ds2-4~jan+lint1_all.deb", ), ("deb", "bcolz-doc"), ( "arg", "/home/janitor/tmp/tmppvupofwl/build-area/python-" "bcolz-dbgsym_1.2.1+ds2-4~jan+lint1_amd64.deb", ), ("deb", "python-bcolz-dbgsym"), ( "arg", "/home/janitor/tmp/" "tmppvupofwl/build-area/python-bcolz_1.2.1+ds2-4~jan" "+lint1_amd64.deb", ), ("deb", "python-bcolz"), ( "arg", "/home/janitor/tmp/tmppvupofwl/build-area/" "python3-bcolz-dbgsym_1.2.1+ds2-4~jan+lint1_amd64.deb", ), ("deb", "python3-bcolz-dbgsym"), ( "arg", "/home/janitor/tmp/tmppvupofwl/build-area/python3-" "bcolz_1.2.1+ds2-4~jan+lint1_amd64.deb", ), ("deb", "python3-bcolz"), ( None, "/home/janitor/tmp/tmppvupofwl/build-area/" "bcolz_1.2.1+ds2-4~jan+lint1.dsc", ), ] ) self.assertEqual( ( SingleLineMatch( 2, "blame: arg:/home/janitor/tmp/tmppvupofwl/build-area/" "bcolz-doc_1.2.1+ds2-4~jan+lint1_all.deb deb:bcolz-doc " "arg:/home/janitor/tmp/tmppvupofwl/build-area/python-" "bcolz-dbgsym_1.2.1+ds2-4~jan+lint1_amd64.deb " "deb:python-bcolz-dbgsym arg:/home/janitor/tmp/" "tmppvupofwl/build-area/python-bcolz_1.2.1+ds2-4~jan" "+lint1_amd64.deb deb:python-bcolz arg:/home/janitor/" "tmp/tmppvupofwl/build-area/python3-bcolz-dbgsym_1.2.1" "+ds2-4~jan+lint1_amd64.deb deb:python3-bcolz-dbgsym " "arg:/home/janitor/tmp/tmppvupofwl/build-area/python3-" "bcolz_1.2.1+ds2-4~jan+lint1_amd64.deb deb:python3-" "bcolz /home/janitor/tmp/tmppvupofwl/build-area/" "bcolz_1.2.1+ds2-4~jan+lint1.dsc\n", ), "python-bcolz", error, "Test python-bcolz failed: Test dependencies are unsatisfiable. " "A common reason is that your testbed is out of date " "with respect to the archive, and you need to use a " "current testbed or run apt-get update or use -U.", ), find_autopkgtest_failure_description( [ "autopkgtest [07:58:03]: @@@@@@@@@@@@@@@@@@@@ summary\n", "python-bcolz FAIL badpkg\n", "blame: arg:/home/janitor/tmp/tmppvupofwl/build-area/" "bcolz-doc_1.2.1+ds2-4~jan+lint1_all.deb deb:bcolz-doc " "arg:/home/janitor/tmp/tmppvupofwl/build-area/python-" "bcolz-dbgsym_1.2.1+ds2-4~jan+lint1_amd64.deb " "deb:python-bcolz-dbgsym arg:/home/janitor/tmp/" "tmppvupofwl/build-area/python-bcolz_1.2.1+ds2-4~jan" "+lint1_amd64.deb deb:python-bcolz arg:/home/janitor/" "tmp/tmppvupofwl/build-area/python3-bcolz-dbgsym_1.2.1" "+ds2-4~jan+lint1_amd64.deb deb:python3-bcolz-dbgsym " "arg:/home/janitor/tmp/tmppvupofwl/build-area/python3-" "bcolz_1.2.1+ds2-4~jan+lint1_amd64.deb deb:python3-" "bcolz /home/janitor/tmp/tmppvupofwl/build-area/" "bcolz_1.2.1+ds2-4~jan+lint1.dsc\n", "badpkg: Test dependencies are unsatisfiable. " "A common reason is that your testbed is out of date " "with respect to the archive, and you need to use a " "current testbed or run apt-get update or use -U.\n", ] ), ) error = AutopkgtestDepsUnsatisfiable( [ ( "arg", "/home/janitor/tmp/tmpgbn5jhou/build-area/cmake" "-extras_1.3+17.04.20170310-6~jan+unchanged1_all.deb", ), ("deb", "cmake-extras"), ( None, "/home/janitor/tmp/tmpgbn5jhou/" "build-area/cmake-extras_1.3+17.04.20170310-6~jan.dsc", ), ] ) self.assertEqual( ( SingleLineMatch( 2, "blame: arg:/home/janitor/tmp/tmpgbn5jhou/build-area/cmake" "-extras_1.3+17.04.20170310-6~jan+unchanged1_all.deb " "deb:cmake-extras /home/janitor/tmp/tmpgbn5jhou/" "build-area/cmake-extras_1.3+17.04.20170310-6~jan.dsc", ), "intltool", error, "Test intltool failed: Test dependencies are unsatisfiable. " "A common reason is that your testbed is out of date with " "respect to the archive, and you need to use a current testbed " "or run apt-get update or use -U.", ), find_autopkgtest_failure_description( [ "autopkgtest [07:58:03]: @@@@@@@@@@@@@@@@@@@@ summary\n", "intltool FAIL badpkg", "blame: arg:/home/janitor/tmp/tmpgbn5jhou/build-area/cmake" "-extras_1.3+17.04.20170310-6~jan+unchanged1_all.deb " "deb:cmake-extras /home/janitor/tmp/tmpgbn5jhou/" "build-area/cmake-extras_1.3+17.04.20170310-6~jan.dsc", "badpkg: Test dependencies are unsatisfiable. A common " "reason is that your testbed is out of date with respect " "to the archive, and you need to use a current testbed or " "run apt-get update or use -U.", ] ), ) def test_session_disappeared(self): error = AutopkgtestDepChrootDisappeared() self.assertEqual( ( SingleLineMatch( 3, ": failure: ['chmod', '1777', '/tmp/autopkgtest.JLqPpH'] unexpectedly produced stderr output `W: /var/lib/schroot/session/unstable-amd64-sbuild-dbcdb3f2-53ed-4f84-8f0d-2c53ebe71010: Failed to stat file: No such file or directory", ), None, error, ": failure: ['chmod', '1777', '/tmp/autopkgtest.JLqPpH'] unexpectedly produced stderr output `W: /var/lib/schroot/session/unstable-amd64-sbuild-dbcdb3f2-53ed-4f84-8f0d-2c53ebe71010: Failed to stat file: No such file or directory", ), find_autopkgtest_failure_description( """\ autopkgtest [22:52:18]: starting date: 2021-04-01 autopkgtest [22:52:18]: version 5.16 autopkgtest [22:52:18]: host osuosl167-amd64; command line: /usr/bin/autopkgtest '/tmp/tmpb0o8ai2j/build-area/liquid-dsp_1.2.0+git20210131.9ae84d8-1~jan+deb1_amd64.changes' --no-auto-control -- schroot unstable-amd64-sbuild : failure: ['chmod', '1777', '/tmp/autopkgtest.JLqPpH'] unexpectedly produced stderr output `W: /var/lib/schroot/session/unstable-amd64-sbuild-dbcdb3f2-53ed-4f84-8f0d-2c53ebe71010: Failed to stat file: No such file or directory ' autopkgtest [22:52:19]: ERROR: testbed failure: cannot send to testbed: [Errno 32] Broken pipe """.splitlines( False ) ), ) def test_stderr(self): error = AutopkgtestStderrFailure("some output") self.assertEqual( ( SingleLineMatch(2, "some output"), "intltool", error, "Test intltool failed due to unauthorized stderr output: " "some output", ), find_autopkgtest_failure_description( [ "intltool FAIL stderr: some output", "autopkgtest [20:49:00]: test intltool:" " - - - - - - - - - - stderr - - - - - - - - - -", "some output", "some more output", "autopkgtest [20:49:00]: @@@@@@@@@@@@@@@@@@@@ summary", "intltool FAIL stderr: some output", ] ), ) self.assertEqual( ( SingleLineMatch(1, "/tmp/bla: 12: ss: not found"), "intltool", MissingCommand("ss"), "/tmp/bla: 12: ss: not found", ), find_autopkgtest_failure_description( [ "autopkgtest [20:49:00]: test intltool:" " - - - - - - - - - - stderr - - - - - - - - - -", "/tmp/bla: 12: ss: not found", "some more output", "autopkgtest [20:49:00]: @@@@@@@@@@@@@@@@@@@@ summary", "intltool FAIL stderr: /tmp/bla: 12: ss: not found", ] ), ) self.assertEqual( ( SingleLineMatch( 1, 'command10 FAIL stderr: Can\'t exec "uptime": No such file or directory at ' "/usr/lib/nagios/plugins/check_uptime line 529.", ), "command10", MissingCommand("uptime"), 'Can\'t exec "uptime": No such file or directory at ' "/usr/lib/nagios/plugins/check_uptime line 529.", ), find_autopkgtest_failure_description( [ "autopkgtest [07:58:03]: @@@@@@@@@@@@@@@@@@@@ summary\n", 'command10 FAIL stderr: Can\'t exec "uptime": ' "No such file or directory at " "/usr/lib/nagios/plugins/check_uptime line 529.", ] ), ) def test_testbed_failure(self): error = AutopkgtestTestbedFailure( "sent `copyup /tmp/autopkgtest.9IStGJ/build.0Pm/src/ " "/tmp/autopkgtest.output.icg0g8e6/tests-tree/', got " "`timeout', expected `ok...'" ) self.assertEqual( ( SingleLineMatch( 0, "autopkgtest [12:46:18]: ERROR: testbed failure: sent " "`copyup /tmp/autopkgtest.9IStGJ/build.0Pm/src/ " "/tmp/autopkgtest.output.icg0g8e6/tests-tree/', got " "`timeout', expected `ok...'\n", ), None, error, None, ), find_autopkgtest_failure_description( [ "autopkgtest [12:46:18]: ERROR: testbed failure: sent " "`copyup /tmp/autopkgtest.9IStGJ/build.0Pm/src/ " "/tmp/autopkgtest.output.icg0g8e6/tests-tree/', got " "`timeout', expected `ok...'\n" ] ), ) def test_testbed_failure_with_test(self): error = AutopkgtestTestbedFailure("testbed auxverb failed with exit code 255") self.assertEqual( ( SingleLineMatch( 3, "autopkgtest [06:59:01]: ERROR: testbed failure: testbed auxverb failed with exit code 255\n", ), "phpunit", error, None, ), find_autopkgtest_failure_description( """\ Removing autopkgtest-satdep (0) ... autopkgtest [06:59:00]: test phpunit: [----------------------- PHP Fatal error: Declaration of Wicked_TestCase::setUp() must \ be compatible with PHPUnit\\Framework\\TestCase::setUp(): void in \ /tmp/autopkgtest.5ShOBp/build.ViG/src/wicked-2.0.8/test/Wicked/\ TestCase.php on line 31 autopkgtest [06:59:01]: ERROR: testbed failure: testbed auxverb \ failed with exit code 255 Exiting with 16 """.splitlines( True ) ), ) def test_test_command_failure(self): self.assertEqual( ( SingleLineMatch( 5, 'Cannot open file "/usr/share/php/Pimple/autoload.php".\n' ), "command2", MissingFile("/usr/share/php/Pimple/autoload.php"), 'Cannot open file "/usr/share/php/Pimple/autoload.php".\n', ), find_autopkgtest_failure_description( """\ Removing autopkgtest-satdep (0) ... autopkgtest [01:30:11]: test command2: phpunit --bootstrap /usr/autoload.php autopkgtest [01:30:11]: test command2: [----------------------- PHPUnit 8.5.2 by Sebastian Bergmann and contributors. Cannot open file "/usr/share/php/Pimple/autoload.php". autopkgtest [01:30:12]: test command2: -----------------------] autopkgtest [01:30:12]: test command2: \ - - - - - - - - - - results - - - - - - - - - - command2 FAIL non-zero exit status 1 autopkgtest [01:30:12]: @@@@@@@@@@@@@@@@@@@@ summary command1 PASS command2 FAIL non-zero exit status 1 Exiting with 4 """.splitlines( True ) ), ) def test_dpkg_failure(self): self.assertEqual( ( SingleLineMatch( 7, 'autopkgtest [19:19:23]: ERROR: "dpkg --unpack ' '/tmp/autopkgtest.hdIETy/4-autopkgtest-satdep.deb" failed with ' 'stderr "W: /var/lib/schroot/session/unstable-amd64-sbuild-' "7fb1b836-14f9-4709-8584-cbbae284db97: Failed to stat file: " "No such file or directory\n", ), "runtestsuite", AutopkgtestDepChrootDisappeared(), """\ W: /var/lib/schroot/session/unstable-amd64-\ sbuild-7fb1b836-14f9-4709-8584-cbbae284db97: \ Failed to stat file: No such file or directory""", ), find_autopkgtest_failure_description( """\ autopkgtest [19:19:19]: test require: [----------------------- autopkgtest [19:19:20]: test require: -----------------------] autopkgtest [19:19:20]: test require: \ - - - - - - - - - - results - - - - - - - - - - require PASS autopkgtest [19:19:20]: test runtestsuite: preparing testbed Get:1 file:/tmp/autopkgtest.hdIETy/binaries InRelease Ign:1 file:/tmp/autopkgtest.hdIETy/binaries InRelease autopkgtest [19:19:23]: ERROR: "dpkg --unpack \ /tmp/autopkgtest.hdIETy/4-autopkgtest-satdep.deb" failed with \ stderr "W: /var/lib/schroot/session/unstable-amd64-sbuild-\ 7fb1b836-14f9-4709-8584-cbbae284db97: Failed to stat file: \ No such file or directory """.splitlines( True ) ), ) def test_last_stderr_line(self): self.assertEqual( ( SingleLineMatch( 10, "unmunge FAIL non-zero exit status 2\n" ), "unmunge", None, "Test unmunge failed: non-zero exit status 2", ), find_autopkgtest_failure_description( """\ autopkgtest [17:38:49]: test unmunge: [----------------------- munge: Error: Failed to access "/run/munge/munge.socket.2": \ No such file or directory unmunge: Error: No credential specified autopkgtest [17:38:50]: test unmunge: -----------------------] autopkgtest [17:38:50]: test unmunge: \ - - - - - - - - - - results - - - - - - - - - - unmunge FAIL non-zero exit status 2 autopkgtest [17:38:50]: test unmunge: \ - - - - - - - - - - stderr - - - - - - - - - - munge: Error: Failed to access "/run/munge/munge.socket.2": \ No such file or directory unmunge: Error: No credential specified autopkgtest [17:38:50]: @@@@@@@@@@@@@@@@@@@@ summary unmunge FAIL non-zero exit status 2 Exiting with 4 """.splitlines( True ) ), ) def test_python_error_in_output(self): self.assertEqual( ( SingleLineMatch( 5, "builtins.OverflowError: mktime argument out of range\n" ), "unit-tests-3", None, "builtins.OverflowError: mktime argument out of range\n", ), find_autopkgtest_failure_description( """\ autopkgtest [14:55:35]: test unit-tests-3: [----------------------- File "twisted/test/test_log.py", line 511, in test_getTimezoneOffsetWithout self._getTimezoneOffsetTest("Africa/Johannesburg", -7200, -7200) File "twisted/test/test_log.py", line 460, in _getTimezoneOffsetTest daylight = time.mktime(localDaylightTuple) builtins.OverflowError: mktime argument out of range ------------------------------------------------------------------------------- Ran 12377 tests in 143.490s 143.4904797077179 12377 12377 1 0 2352 autopkgtest [14:58:01]: test unit-tests-3: -----------------------] autopkgtest [14:58:01]: test unit-tests-3: \ - - - - - - - - - - results - - - - - - - - - - unit-tests-3 FAIL non-zero exit status 1 autopkgtest [14:58:01]: @@@@@@@@@@@@@@@@@@@@ summary unit-tests-3 FAIL non-zero exit status 1 Exiting with 4 """.splitlines( True ) ), ) buildlog-consultant_0.0.18.orig/buildlog_consultant/tests/test_common.py0000644000000000000000000020457114205553356023677 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # # 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 from ..common import ( CMakeFilesMissing, CMakeNeedExactVersion, find_build_failure_description, CcacheError, DebhelperPatternNotFound, DisappearedSymbols, DuplicateDHCompatLevel, DhLinkDestinationIsDirectory, MismatchGettextVersions, MissingBuildFile, MissingConfigure, MissingJavaScriptRuntime, MissingJVM, MissingConfigStatusInput, MissingCHeader, MissingDHCompatLevel, MissingJDKFile, MissingJDK, MissingJRE, MissingIntrospectionTypelib, MissingPythonModule, MissingPythonDistribution, MissingGoPackage, MissingFile, MissingMavenArtifacts, MissingNodeModule, MissingCommand, MissingCommandOrBuildFile, MissingPkgConfig, MissingBoostComponents, MissingVcVersionerVersion, MissingPerlFile, MissingPerlModule, MissingPerlPredeclared, MissingLatexFile, MissingPhpClass, MissingRubyGem, MissingSetupPyCommand, MissingValaPackage, MissingXmlEntity, MissingVagueDependency, MissingLibrary, MissingJavaClass, MissingRPackage, MissingAutoconfMacro, MissingSprocketsFile, MissingAutomakeInput, MissingGoModFile, NeedPgBuildExtUpdateControl, DhMissingUninstalled, DhUntilUnsupported, DhAddonLoadFailure, NoSpaceOnDevice, DhWithOrderIncorrect, UpstartFilePresent, DirectoryNonExistant, UnknownCertificateAuthority, MissingGitIdentity, VcsControlDirectoryNeeded, ) import unittest class FindBuildFailureDescriptionTests(unittest.TestCase): def run_test(self, lines, lineno, err=None): (match, actual_err) = find_build_failure_description(lines) if match is not None: self.assertEqual(match.line, lines[lineno - 1]) self.assertEqual(lineno, match.lineno) else: self.assertIsNone(match) if err: self.assertEqual(actual_err, err) else: self.assertIs(None, actual_err) def test_make_missing_rule(self): self.run_test( [ "make[1]: *** No rule to make target 'nno.autopgen.bin', " "needed by 'dan-nno.autopgen.bin'. Stop." ], 1, MissingBuildFile('nno.autopgen.bin'), ) self.run_test( [ "make[1]: *** No rule to make target '/usr/share/blah/blah', " "needed by 'dan-nno.autopgen.bin'. Stop." ], 1, MissingFile("/usr/share/blah/blah"), ) self.run_test( [ "debian/rules:4: /usr/share/openstack-pkg-tools/pkgos.make: " "No such file or directory" ], 1, MissingFile("/usr/share/openstack-pkg-tools/pkgos.make"), ) def test_git_identity(self): self.run_test( [ "fatal: unable to auto-detect email address " "(got 'jenkins@osuosl167-amd64.(none)')" ], 1, MissingGitIdentity(), ) def test_ioerror(self): self.run_test( [ "E IOError: [Errno 2] No such file or directory: " "'/usr/lib/python2.7/poly1305/rfc7539.txt'" ], 1, MissingFile("/usr/lib/python2.7/poly1305/rfc7539.txt"), ) def test_upstart_file_present(self): self.run_test( [ "dh_installinit: upstart jobs are no longer supported! " "Please remove debian/sddm.upstart and check if you " "need to add a conffile removal" ], 1, UpstartFilePresent("debian/sddm.upstart"), ) def test_missing_go_mod_file(self): self.run_test( [ "go: go.mod file not found in current directory or any " "parent directory; see 'go help modules'" ], 1, MissingGoModFile()) def test_missing_javascript_runtime(self): self.run_test( [ "ExecJS::RuntimeUnavailable: " "Could not find a JavaScript runtime. " "See https://github.com/rails/execjs for a list " "of available runtimes." ], 1, MissingJavaScriptRuntime(), ) def test_directory_missing(self): self.run_test( [ "debian/components/build: 19: cd: can't cd to rollup-plugin", ], 1, DirectoryNonExistant("rollup-plugin"), ) def test_vcs_control_directory(self): self.run_test( [" > Cannot find '.git' directory"], 1, VcsControlDirectoryNeeded(['git'])) def test_missing_sprockets_file(self): self.run_test( [ "Sprockets::FileNotFound: couldn't find file " "'activestorage' with type 'application/javascript'" ], 1, MissingSprocketsFile("activestorage", "application/javascript"), ) def test_gxx_missing_file(self): self.run_test( [ "g++: error: /usr/lib/x86_64-linux-gnu/libGL.so: " "No such file or directory" ], 1, MissingFile("/usr/lib/x86_64-linux-gnu/libGL.so"), ) def test_build_xml_missing_file(self): self.run_test( ["/<>/build.xml:59: " "/<>/lib does not exist."], 1, MissingBuildFile('lib') ) def test_vignette_builder(self): self.run_test( [" vignette builder 'R.rsp' not found"], 1, MissingRPackage("R.rsp") ) def test_dh_missing_addon(self): self.run_test( [ " dh_auto_clean -O--buildsystem=pybuild", "E: Please add appropriate interpreter package to Build-Depends, " "see pybuild(1) for details.this: $VAR1 = bless( {", " 'py3vers' => '3.8',", " 'py3def' => '3.8',", " 'pyvers' => '',", " 'parallel' => '2',", " 'cwd' => '/<>',", " 'sourcedir' => '.',", " 'builddir' => undef,", " 'pypydef' => '',", " 'pydef' => ''", " }, 'Debian::Debhelper::Buildsystem::pybuild' );", "deps: $VAR1 = [];", ], 2, DhAddonLoadFailure("pybuild", "Debian/Debhelper/Buildsystem/pybuild.pm"), ) def test_libtoolize_missing_file(self): self.run_test( ["libtoolize: error: '/usr/share/aclocal/ltdl.m4' " "does not exist."], 1, MissingFile("/usr/share/aclocal/ltdl.m4"), ) def test_ruby_missing_file(self): self.run_test( [ "Error: Error: ENOENT: no such file or directory, " "open '/usr/lib/nodejs/requirejs/text.js'" ], 1, MissingFile("/usr/lib/nodejs/requirejs/text.js"), ) def test_vcversioner(self): self.run_test( [ "vcversioner: ['git', '--git-dir', '/build/tmp0tlam4pe/pyee/.git', " "'describe', '--tags', '--long'] failed and " "'/build/tmp0tlam4pe/pyee/version.txt' isn't present." ], 1, MissingVcVersionerVersion(), ) def test_python_missing_file(self): self.run_test( [ "python3.7: can't open file '/usr/bin/blah.py': " "[Errno 2] No such file or directory" ], 1, MissingFile("/usr/bin/blah.py"), ) self.run_test( [ "python3.7: can't open file 'setup.py': " "[Errno 2] No such file or directory" ], 1, MissingBuildFile('setup.py') ) self.run_test( [ "E FileNotFoundError: [Errno 2] " "No such file or directory: " "'/usr/share/firmware-microbit-micropython/firmware.hex'" ], 1, MissingFile("/usr/share/firmware-microbit-micropython/firmware.hex"), ) def test_vague(self): self.run_test( [ "configure: error: Please install gnu flex from http://www.gnu.org/software/flex/" ], 1, MissingVagueDependency("gnu flex", "http://www.gnu.org/software/flex/"), ) self.run_test( ["RuntimeError: cython is missing"], 1, MissingVagueDependency("cython")) self.run_test( [ "configure: error:", "", " Unable to find the Multi Emulator Super System (MESS).", ], 3, MissingVagueDependency("the Multi Emulator Super System (MESS)"), ) self.run_test( ["configure: error: libwandio 4.0.0 or better is required to compile " "this version of libtrace. If you have installed libwandio in a " "non-standard location please use LDFLAGS to specify the location of " "the library. WANDIO can be obtained from " "http://research.wand.net.nz/software/libwandio.php"], 1, MissingVagueDependency("libwandio", minimum_version="4.0.0")) self.run_test( ["configure: error: libpcap0.8 or greater is required to compile " "libtrace. If you have installed it in a non-standard location please " "use LDFLAGS to specify the location of the library"], 1, MissingVagueDependency("libpcap0.8")) def test_gettext_mismatch(self): self.run_test( ["*** error: gettext infrastructure mismatch: using a " "Makefile.in.in from gettext version 0.19 but the autoconf " "macros are from gettext version 0.20"], 1, MismatchGettextVersions('0.19', '0.20')) def test_multi_line_configure_error(self): self.run_test(["configure: error:", "", " Some other error."], 3, None) self.run_test([ "configure: error:", "", " Unable to find the Multi Emulator Super System (MESS).", "", " Please install MESS, or specify the MESS command with", " a MESS environment variable.", "", "e.g. MESS=/path/to/program/mess ./configure" ], 3, MissingVagueDependency("the Multi Emulator Super System (MESS)")) def test_interpreter_missing(self): self.run_test( [ "/bin/bash: /usr/bin/rst2man: /usr/bin/python: " "bad interpreter: No such file or directory" ], 1, MissingFile("/usr/bin/python"), ) self.run_test( ["env: ‘/<>/socket-activate’: " "No such file or directory"], 1, None, ) def test_webpack_missing(self): self.run_test( [ "ERROR in Entry module not found: " "Error: Can't resolve 'index.js' in '/<>'" ], 1, None, ) def test_installdocs_missing(self): self.run_test( [ 'dh_installdocs: Cannot find (any matches for) "README.txt" ' "(tried in ., debian/tmp)" ], 1, DebhelperPatternNotFound("README.txt", "installdocs", [".", "debian/tmp"]), ) def test_cmake_missing_file(self): self.run_test( """\ CMake Error at /usr/lib/x86_64-/cmake/Qt5Gui/Qt5GuiConfig.cmake:27 (message): The imported target "Qt5::Gui" references the file "/usr/lib/x86_64-linux-gnu/libEGL.so" but this file does not exist. Possible reasons include: * The file was deleted, renamed, or moved to another location. * An install or uninstall procedure did not complete successfully. * The installation package was faulty and contained "/usr/lib/x86_64-linux-gnu/cmake/Qt5Gui/Qt5GuiConfigExtras.cmake" but not all the files it references. Call Stack (most recent call first): /usr/lib/x86_64-linux-gnu/QtGui/Qt5Gui.cmake:63 (_qt5_Gui_check_file_exists) /usr/lib/x86_64-linux-gnu/QtGui/Qt5Gui.cmake:85 (_qt5gui_find_extra_libs) /usr/lib/x86_64-linux-gnu/QtGui/Qt5Gui.cmake:186 (include) /usr/lib/x86_64-linux-gnu/QtWidgets/Qt5Widgets.cmake:101 (find_package) /usr/lib/x86_64-linux-gnu/Qt/Qt5Config.cmake:28 (find_package) CMakeLists.txt:34 (find_package) dh_auto_configure: cd obj-x86_64-linux-gnu && cmake with args """.splitlines( True ), 16, MissingFile("/usr/lib/x86_64-linux-gnu/libEGL.so"), ) def test_meson_missing_git(self): self.run_test( ["meson.build:13:0: ERROR: Git program not found."], 1, MissingCommand("git"), ) def test_need_pgbuildext(self): self.run_test( [ "Error: debian/control needs updating from debian/control.in. " "Run 'pg_buildext updatecontrol'." ], 1, NeedPgBuildExtUpdateControl("debian/control", "debian/control.in"), ) def test_cmake_missing_command(self): self.run_test( [ " Could NOT find Git (missing: GIT_EXECUTABLE)", "dh_auto_configure: cd obj-x86_64-linux-gnu && cmake with args", ], 1, MissingCommand("git"), ) def test_cmake_missing_include(self): self.run_test( """\ -- Performing Test _OFFT_IS_64BIT -- Performing Test _OFFT_IS_64BIT - Success -- Performing Test HAVE_DATE_TIME -- Performing Test HAVE_DATE_TIME - Success CMake Error at CMakeLists.txt:43 (include): include could not find load file: KDEGitCommitHooks -- Found KF5Activities: /usr/lib/x86_64-linux-gnu/cmake/KF5Activities/KF5ActivitiesConfig.cmake (found version "5.78.0") -- Found KF5Config: /usr/lib/x86_64-linux-gnu/cmake/KF5Config/KF5ConfigConfig.cmake (found version "5.78.0") """.splitlines(True), 8, CMakeFilesMissing(['KDEGitCommitHooks.cmake'])) def test_cmake_missing_cmake_files(self): self.run_test( """\ Could not find a package configuration file provided by "sensor_msgs" with any of the following names: sensor_msgsConfig.cmake sensor_msgs-config.cmake Add the installation prefix of "sensor_msgs" to CMAKE_PREFIX_PATH or set "sensor_msgs_DIR" to a directory containing one of the above files. If "sensor_msgs" provides a separate development package or SDK, be sure it has been installed. dh_auto_configure: cd obj-x86_64-linux-gnu && cmake with args """.splitlines( True ), 1, CMakeFilesMissing(["sensor_msgsConfig.cmake", "sensor_msgs-config.cmake"]), ) def test_cmake_missing_exact_version(self): self.run_test( """\ CMake Error at /usr/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:165 (message): Could NOT find SignalProtocol: Found unsuitable version "2.3.3", but required is exact version "2.3.2" (found /usr/lib/x86_64-linux-gnu/libsignal-protocol-c.so) """.splitlines( True ), 4, CMakeNeedExactVersion( "SignalProtocol", "2.3.3", "2.3.2", "/usr/lib/x86_64-linux-gnu/libsignal-protocol-c.so", ), ) def test_cmake_missing_vague(self): self.run_test( ["CMake Error at CMakeLists.txt:84 (MESSAGE):", " alut not found"], 2, MissingVagueDependency("alut"), ) self.run_test( ["CMake Error at CMakeLists.txt:213 (message):", " could not find zlib"], 2, MissingVagueDependency("zlib")) def test_dh_compat_dupe(self): self.run_test( [ "dh_autoreconf: debhelper compat level specified both in " "debian/compat and via build-dependency on debhelper-compat" ], 1, DuplicateDHCompatLevel("dh_autoreconf"), ) def test_dh_compat_missing(self): self.run_test( ["dh_clean: Please specify the compatibility level in " "debian/compat"], 1, MissingDHCompatLevel("dh_clean"), ) def test_dh_udeb_shared_library(self): self.run_test( [ "dh_makeshlibs: The udeb libepoxy0-udeb (>= 1.3) does not contain" " any shared libraries but --add-udeb=libepoxy0-udeb (>= 1.3) " "was passed!?" ], 1, ) def test_dh_systemd(self): self.run_test( [ "dh: unable to load addon systemd: dh: The systemd-sequence is " "no longer provided in compat >= 11, please rely on " "dh_installsystemd instead" ], 1, ) def test_dh_before(self): self.run_test( [ "dh: The --before option is not supported any longer (#932537). " "Use override targets instead." ], 1, ) def test_distutils_missing(self): self.run_test( [ "distutils.errors.DistutilsError: Could not find suitable " "distribution for Requirement.parse('pytest-runner')" ], 1, MissingPythonDistribution("pytest-runner", None), ) self.run_test( [ "distutils.errors.DistutilsError: Could not find suitable " "distribution for Requirement.parse('certifi>=2019.3.9')" ], 1, MissingPythonDistribution("certifi", None, "2019.3.9"), ) self.run_test( [ "distutils.errors.DistutilsError: Could not find suitable " "distribution for Requirement.parse('cffi; " 'platform_python_implementation == "CPython"\')' ], 1, MissingPythonDistribution("cffi", None), ) self.run_test( [ "error: Could not find suitable distribution for " "Requirement.parse('gitlab')" ], 1, MissingPythonDistribution("gitlab", None), ) self.run_test( [ "pkg_resources.DistributionNotFound: The 'configparser>=3.5' " "distribution was not found and is required by importlib-metadata" ], 1, MissingPythonDistribution("configparser", None, "3.5"), ) self.run_test( [ "error: Command '['/usr/bin/python3.9', '-m', 'pip', " "'--disable-pip-version-check', 'wheel', '--no-deps', '-w', " "'/tmp/tmp973_8lhm', '--quiet', 'asynctest']' " "returned non-zero exit status 1." ], 1, MissingPythonDistribution("asynctest", python_version=3), ) self.run_test( [ "subprocess.CalledProcessError: Command " "'['/usr/bin/python', '-m', 'pip', " "'--disable-pip-version-check', 'wheel', '--no-deps', " "'-w', '/tmp/tmpm2l3kcgv', '--quiet', 'setuptools_scm']' " "returned non-zero exit status 1." ], 1, MissingPythonDistribution("setuptools_scm"), ) def test_lazy_font(self): self.run_test( [ "[ERROR] LazyFont - Failed to read font file " "/usr/share/texlive/texmf-dist/fonts/opentype/public/" "stix2-otf/STIX2Math.otf " "java.io.FileNotFoundException: " "/usr/share/texlive/texmf-dist/fonts/opentype/public/stix2-otf" "/STIX2Math.otf (No such file or directory)" ], 1, MissingFile( "/usr/share/texlive/texmf-dist/fonts/opentype/" "public/stix2-otf/STIX2Math.otf" ), ) def test_missing_latex_files(self): self.run_test( ["! LaTeX Error: File `fancyvrb.sty' not found."], 1, MissingLatexFile("fancyvrb.sty"), ) def test_pytest_import(self): self.run_test( ["E ImportError: cannot import name cmod"], 1, MissingPythonModule("cmod") ) self.run_test( ["E ImportError: No module named mock"], 1, MissingPythonModule("mock") ) self.run_test( [ "pluggy.manager.PluginValidationError: " "Plugin 'xdist.looponfail' could not be loaded: " "(pytest 3.10.1 (/usr/lib/python2.7/dist-packages), " "Requirement.parse('pytest>=4.4.0'))!" ], 1, MissingPythonModule("pytest", 2, "4.4.0"), ) self.run_test( [ "ImportError: Error importing plugin " '"tests.plugins.mock_libudev": No module named mock' ], 1, MissingPythonModule("mock"), ) def test_sed(self): self.run_test( ["sed: can't read /etc/locale.gen: No such file or directory"], 1, MissingFile("/etc/locale.gen"), ) def test_python2_import(self): self.run_test( ["ImportError: No module named pytz"], 1, MissingPythonModule("pytz") ) self.run_test(["ImportError: cannot import name SubfieldBase"], 1, None) def test_python3_import(self): self.run_test( ["ModuleNotFoundError: No module named 'django_crispy_forms'"], 1, MissingPythonModule("django_crispy_forms", 3), ) self.run_test( [" ModuleNotFoundError: No module named 'Cython'"], 1, MissingPythonModule("Cython", 3), ) self.run_test( ["ModuleNotFoundError: No module named 'distro'"], 1, MissingPythonModule("distro", 3), ) self.run_test( ["E ModuleNotFoundError: No module named 'twisted'"], 1, MissingPythonModule("twisted", 3), ) self.run_test( [ "E ImportError: cannot import name 'async_poller' " "from 'msrest.polling' " "(/usr/lib/python3/dist-packages/msrest/polling/__init__.py)" ], 1, MissingPythonModule("msrest.polling.async_poller"), ) self.run_test( ["/usr/bin/python3: No module named sphinx"], 1, MissingPythonModule("sphinx", 3), ) self.run_test( [ "Could not import extension sphinx.ext.pngmath (exception: " "No module named pngmath)" ], 1, MissingPythonModule("pngmath"), ) self.run_test( [ "/usr/bin/python3: Error while finding module specification " "for 'pep517.build' " "(ModuleNotFoundError: No module named 'pep517')" ], 1, MissingPythonModule("pep517", python_version=3), ) def test_sphinx(self): self.run_test( [ "There is a syntax error in your configuration file: " "Unknown syntax: Constant" ], 1, None, ) def test_go_missing(self): self.run_test( [ "src/github.com/vuls/config/config.go:30:2: cannot find package " '"golang.org/x/xerrors" in any of:' ], 1, MissingGoPackage("golang.org/x/xerrors"), ) def test_c_header_missing(self): self.run_test( ["cdhit-common.h:39:9: fatal error: zlib.h: No such file " "or directory"], 1, MissingCHeader("zlib.h"), ) self.run_test( [ "/<>/Kernel/Operation_Vector.cpp:15:10: " "fatal error: petscvec.h: No such file or directory" ], 1, MissingCHeader("petscvec.h"), ) self.run_test( [ "src/bubble.h:27:10: fatal error: DBlurEffectWidget: " "No such file or directory" ], 1, MissingCHeader("DBlurEffectWidget"), ) def test_missing_jdk_file(self): self.run_test( [ "> Could not find tools.jar. Please check that " "/usr/lib/jvm/java-8-openjdk-amd64 contains a " "valid JDK installation.", ], 1, MissingJDKFile("/usr/lib/jvm/java-8-openjdk-amd64", "tools.jar"), ) def test_missing_jdk(self): self.run_test( [ "> Kotlin could not find the required JDK tools in " "the Java installation " "'/usr/lib/jvm/java-8-openjdk-amd64/jre' used by Gradle. " "Make sure Gradle is running on a JDK, not JRE.", ], 1, MissingJDK("/usr/lib/jvm/java-8-openjdk-amd64/jre"), ) def test_missing_jre(self): self.run_test( [ "ERROR: JAVA_HOME is not set and no 'java' command " "could be found in your PATH." ], 1, MissingJRE(), ) def test_node_module_missing(self): self.run_test( ["Error: Cannot find module 'tape'"], 1, MissingNodeModule("tape") ) self.run_test( [ "✖ ERROR: Cannot find module '/<>/test'", ], 1, None, ) self.run_test( ["npm ERR! [!] Error: Cannot find module '@rollup/plugin-buble'"], 1, MissingNodeModule("@rollup/plugin-buble"), ) self.run_test( ["npm ERR! Error: Cannot find module 'fs-extra'"], 1, MissingNodeModule("fs-extra"), ) self.run_test( ["\x1b[1m\x1b[31m[!] \x1b[1mError: Cannot find module '@rollup/plugin-buble'"], 1, MissingNodeModule('@rollup/plugin-buble')) def test_setup_py_command(self): self.run_test( """\ /usr/lib/python3.9/distutils/dist.py:274: UserWarning: Unknown distribution option: 'long_description_content_type' warnings.warn(msg) /usr/lib/python3.9/distutils/dist.py:274: UserWarning: Unknown distribution option: 'test_suite' warnings.warn(msg) /usr/lib/python3.9/distutils/dist.py:274: UserWarning: Unknown distribution option: 'python_requires' warnings.warn(msg) usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: setup.py --help [cmd1 cmd2 ...] or: setup.py --help-commands or: setup.py cmd --help error: invalid command 'test' """.splitlines( True ), 12, MissingSetupPyCommand("test"), ) def test_command_missing(self): self.run_test( ["./ylwrap: line 176: yacc: command not found"], 1, MissingCommand("yacc") ) self.run_test(["/bin/sh: 1: cmake: not found"], 1, MissingCommand("cmake")) self.run_test(["sh: 1: git: not found"], 1, MissingCommand("git")) self.run_test( ["/usr/bin/env: ‘python3’: No such file or directory"], 1, MissingCommand("python3"), ) self.run_test( ["%Error: 'flex' must be installed to build"], 1, MissingCommand("flex") ) self.run_test( ['pkg-config: exec: "pkg-config": executable file not found in $PATH'], 1, MissingCommand("pkg-config"), ) self.run_test( ['Can\'t exec "git": No such file or directory at ' "Makefile.PL line 25."], 1, MissingCommand("git"), ) self.run_test( [ "vcver.scm.git.GitCommandError: 'git describe --tags --match 'v*'" " --abbrev=0' returned an error code 127" ], 1, MissingCommand("git"), ) self.run_test( ["make[1]: docker: Command not found"], 1, MissingCommand("docker") ) self.run_test(["make[1]: git: Command not found"], 1, MissingCommand("git")) self.run_test(["make[1]: ./docker: Command not found"], 1, None) self.run_test( ["make: dh_elpa: Command not found"], 1, MissingCommand("dh_elpa") ) self.run_test( ["/bin/bash: valac: command not found"], 1, MissingCommand("valac") ) self.run_test( ["E: Failed to execute “python3”: No such file or directory"], 1, MissingCommand("python3"), ) self.run_test( [ 'Can\'t exec "cmake": No such file or directory at ' "/usr/share/perl5/Debian/Debhelper/Dh_Lib.pm line 484." ], 1, MissingCommand("cmake"), ) self.run_test( [ "Invalid gemspec in [unicorn.gemspec]: " "No such file or directory - git" ], 1, MissingCommand("git"), ) self.run_test( [ "dbus-run-session: failed to exec 'xvfb-run': " "No such file or directory" ], 1, MissingCommand("xvfb-run"), ) self.run_test(["/bin/sh: 1: ./configure: not found"], 1, MissingConfigure()) self.run_test( ["xvfb-run: error: xauth command not found"], 1, MissingCommand("xauth") ) self.run_test( [ "meson.build:39:2: ERROR: Program(s) ['wrc'] " "not found or not executable" ], 1, MissingCommand("wrc"), ) self.run_test( [ "/tmp/autopkgtest.FnbV06/build.18W/src/debian/tests/" "blas-testsuite: 7: dpkg-architecture: not found" ], 1, MissingCommand("dpkg-architecture"), ) self.run_test( [ "Traceback (most recent call last):", ' File "/usr/lib/python3/dist-packages/mesonbuild/mesonmain.py", line 140, in run', " return options.run_func(options)", ' File "/usr/lib/python3/dist-packages/mesonbuild/mdist.py", line 267, in run', " names = create_dist_git(dist_name, archives, src_root, bld_root, dist_sub, b.dist_scripts, subprojects)", ' File "/usr/lib/python3/dist-packages/mesonbuild/mdist.py", line 119, in create_dist_git', " git_clone(src_root, distdir)", ' File "/usr/lib/python3/dist-packages/mesonbuild/mdist.py", line 108, in git_clone', " if git_have_dirty_index(src_root):", ' File "/usr/lib/python3/dist-packages/mesonbuild/mdist.py", line 104, in git_have_dirty_index', " ret = subprocess.call(['git', '-C', src_root, 'diff-index', '--quiet', 'HEAD'])", ' File "/usr/lib/python3.9/subprocess.py", line 349, in call', " with Popen(*popenargs, **kwargs) as p:", ' File "/usr/lib/python3.9/subprocess.py", line 951, in __init__', " self._execute_child(args, executable, preexec_fn, close_fds,", ' File "/usr/lib/python3.9/subprocess.py", line 1823, in _execute_child', " raise child_exception_type(errno_num, err_msg, err_filename)", "FileNotFoundError: [Errno 2] No such file or directory: 'git'", ], 18, MissingCommand("git"), ) self.run_test( ['> Cannot run program "git": error=2, No such file or directory'], 1, MissingCommand("git"), ) def test_ts_error(self): self.run_test( [ "blah/tokenizer.ts(175,21): error TS2532: " "Object is possibly 'undefined'." ], 1, None, ) def test_nim_error(self): self.run_test( [ "/<>/msgpack4nim.nim(470, 6) " "Error: usage of 'isNil' is a user-defined error" ], 1, None, ) def test_scala_error(self): self.run_test( [ "core/src/main/scala/org/json4s/JsonFormat.scala:131: " "error: No JSON deserializer found for type List[T]. " "Try to implement an implicit Reader or JsonFormat for this type." ], 1, None, ) def test_vala_error(self): self.run_test( [ "../src/Backend/FeedServer.vala:60.98-60.148: error: " "The name `COLLECTION_CREATE_NONE' does not exist in " "the context of `Secret.CollectionCreateFlags'" ], 1, None, ) self.run_test( [ "error: Package `glib-2.0' not found in specified Vala " "API directories or GObject-Introspection GIR directories" ], 1, MissingValaPackage("glib-2.0"), ) def test_gir(self): self.run_test( ["ValueError: Namespace GnomeDesktop not available"], 1, MissingIntrospectionTypelib("GnomeDesktop")) def test_missing_boost_components(self): self.run_test("""\ CMake Error at /usr/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:165 (message): Could NOT find Boost (missing: program_options filesystem system graph serialization iostreams) (found suitable version "1.74.0", minimum required is "1.55.0") Call Stack (most recent call first): /usr/share/cmake-3.18/Modules/FindPackageHandleStandardArgs.cmake:458 (_FPHSA_FAILURE_MESSAGE) /usr/share/cmake-3.18/Modules/FindBoost.cmake:2177 (find_package_handle_standard_args) src/CMakeLists.txt:4 (find_package) """.splitlines(True), 4, MissingBoostComponents([ 'program_options', 'filesystem', 'system', 'graph', 'serialization', 'iostreams'])) def test_pkg_config_too_old(self): self.run_test( [ "checking for pkg-config... no", "", "*** Your version of pkg-config is too old. You need atleast", "*** pkg-config 0.9.0 or newer. You can download pkg-config", "*** from the freedesktop.org software repository at", "***", "*** https://www.freedesktop.org/wiki/Software/pkg-config/", "***" ], 4, MissingVagueDependency("pkg-config", minimum_version="0.9.0")) def test_pkg_config_missing(self): self.run_test( [ "configure: error: Package requirements " "(apertium-3.2 >= 3.2.0) were not met:" ], 1, MissingPkgConfig("apertium-3.2", "3.2.0"), ) self.run_test( [ 'meson.build:10:0: ERROR: Dependency "gssdp-1.2" not ' "found, tried pkgconfig" ], 1, MissingPkgConfig("gssdp-1.2"), ) self.run_test( [ "src/plugins/sysprof/meson.build:3:0: " 'ERROR: Dependency "sysprof-3" not found, tried pkgconfig' ], 1, MissingPkgConfig("sysprof-3"), ) self.run_test( [ "meson.build:84:0: ERROR: Invalid version of dependency, " "need 'libpeas-1.0' ['>= 1.24.0'] found '1.22.0'." ], 1, MissingPkgConfig("libpeas-1.0", "1.24.0"), ) self.run_test( [ "meson.build:233:0: ERROR: Invalid version of dependency, need 'vte-2.91' ['>=0.63.0'] found '0.62.3'." ], 1, MissingPkgConfig("vte-2.91", "0.63.0"), ) self.run_test(["No package 'tepl-3' found"], 1, MissingPkgConfig("tepl-3")) self.run_test( ["Requested 'vte-2.91 >= 0.59.0' but version of vte is 0.58.2"], 1, MissingPkgConfig("vte-2.91", "0.59.0"), ) self.run_test( ["configure: error: x86_64-linux-gnu-pkg-config sdl2 couldn't " "be found"], 1, MissingPkgConfig("sdl2"), ) self.run_test( ["configure: error: No package 'libcrypto' found"], 1, MissingPkgConfig("libcrypto"), ) self.run_test( [ "-- Checking for module 'gtk+-3.0'", "-- Package 'gtk+-3.0', required by 'virtual:world', not found", ], 2, MissingPkgConfig("gtk+-3.0"), ) self.run_test( [ "configure: error: libfilezilla not found: Package dependency " "requirement 'libfilezilla >= 0.17.1' could not be satisfied." ], 1, MissingPkgConfig("libfilezilla", "0.17.1"), ) def test_pkgconf(self): self.run_test( [ "checking for LAPACK... " 'configure: error: "Cannot check for existence of module lapack without pkgconf"' ], 1, MissingCommand("pkgconf"), ) def test_dh_with_order(self): self.run_test( [ "dh: Unknown sequence --with " "(options should not come before the sequence)" ], 1, DhWithOrderIncorrect(), ) def test_no_disk_space(self): self.run_test( [ "/usr/bin/install: error writing '" "/<>/debian/tmp/usr/lib/gcc/" "x86_64-linux-gnu/8/cc1objplus': No space left on device" ], 1, NoSpaceOnDevice(), ) self.run_test( [ "OSError: [Errno 28] No space left on device", ], 1, NoSpaceOnDevice(), ) def test_segmentation_fault(self): self.run_test( [ "/bin/bash: line 3: 7392 Segmentation fault " 'itstool -m "${mo}" ${d}/C/index.docbook ${d}/C/legal.xml' ], 1, ) def test_missing_perl_plugin(self): self.run_test( [ "Required plugin bundle Dist::Zilla::PluginBundle::Git isn't " "installed." ], 1, MissingPerlModule(None, "Dist::Zilla::PluginBundle::Git", None), ) self.run_test( ["Required plugin Dist::Zilla::Plugin::PPPort isn't installed."], 1, MissingPerlModule(filename=None, module="Dist::Zilla::Plugin::PPPort"), ) def test_perl_expand(self): self.run_test( [">(error): Could not expand [ 'Dist::Inkt::Profile::TOBYINK'"], 1, MissingPerlModule(None, module="Dist::Inkt::Profile::TOBYINK"), ) def test_perl_missing_predeclared(self): self.run_test( [ "String found where operator expected at Makefile.PL line 13, near \"author_tests 'xt'\"", "\t(Do you need to predeclare author_tests?)", "syntax error at Makefile.PL line 13, near \"author_tests 'xt'\"", '"strict subs" in use at Makefile.PL line 13.', ], 2, MissingPerlPredeclared("author_tests"), ) self.run_test( ["String found where operator expected at Makefile.PL line 8, near \"readme_from 'lib/URL/Encode.pod'\""], 1, MissingPerlPredeclared("readme_from")) self.run_test( [ 'Bareword "use_test_base" not allowed while "strict subs" in use at Makefile.PL line 12.' ], 1, MissingPerlPredeclared("use_test_base"), ) def test_unknown_cert_authority(self): self.run_test( [ "go: github.com/golangci/golangci-lint@v1.24.0: Get " '"https://proxy.golang.org/github.com/golangci/' 'golangci-lint/@v/v1.24.0.mod": x509: ' "certificate signed by unknown authority" ], 1, UnknownCertificateAuthority( "https://proxy.golang.org/github.com/golangci/" "golangci-lint/@v/v1.24.0.mod" ), ) def test_missing_perl_module(self): self.run_test( [ "Converting tags.ledger... Can't locate String/Interpolate.pm in " "@INC (you may need to install the String::Interpolate module) " "(@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/" "5.28.1 /usr/local/share/perl/5.28.1 /usr/lib/x86_64-linux-gnu/" "perl5/5.28 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.28 " "/usr/share/perl/5.28 /usr/local/lib/site_perl " "/usr/lib/x86_64-linux-gnu/perl-base) at " "../bin/ledger2beancount line 23." ], 1, MissingPerlModule( "String/Interpolate.pm", "String::Interpolate", [ "/etc/perl", "/usr/local/lib/x86_64-linux-gnu/perl/5.28.1", "/usr/local/share/perl/5.28.1", "/usr/lib/x86_64-linux-gnu/perl5/5.28", "/usr/share/perl5", "/usr/lib/x86_64-linux-gnu/perl/5.28", "/usr/share/perl/5.28", "/usr/local/lib/site_perl", "/usr/lib/x86_64-linux-gnu/perl-base", ], ), ) self.run_test( [ "Can't locate Test/Needs.pm in @INC " "(you may need to install the Test::Needs module) " "(@INC contains: t/lib /<>/blib/lib " "/<>/blib/arch /etc/perl " "/usr/local/lib/x86_64-linux-gnu/perl/5.30.0 " "/usr/local/share/perl/5.30.0 /usr/lib/x86_64-linux-gnu/perl5/5.30" " /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.30 " "/usr/share/perl/5.30 /usr/local/lib/site_perl " "/usr/lib/x86_64-linux-gnu/perl-base .) at " "t/anon-basic.t line 7." ], 1, MissingPerlModule( "Test/Needs.pm", "Test::Needs", [ "t/lib", "/<>/blib/lib", "/<>/blib/arch", "/etc/perl", "/usr/local/lib/x86_64-linux-gnu/perl/5.30.0", "/usr/local/share/perl/5.30.0", "/usr/lib/x86_64-linux-gnu/perl5/5.30", "/usr/share/perl5", "/usr/lib/x86_64-linux-gnu/perl/5.30", "/usr/share/perl/5.30", "/usr/local/lib/site_perl", "/usr/lib/x86_64-linux-gnu/perl-base", ".", ], ), ) self.run_test( ["- ExtUtils::Depends ...missing. (would need 0.302)"], 1, MissingPerlModule(None, "ExtUtils::Depends", None, "0.302")) self.run_test( ['Can\'t locate object method "new" via package "Dist::Inkt::Profile::TOBYINK" ' '(perhaps you forgot to load "Dist::Inkt::Profile::TOBYINK"?) at ' '/usr/share/perl5/Dist/Inkt.pm line 208.'], 1, MissingPerlModule(None, "Dist::Inkt::Profile::TOBYINK", None)) self.run_test( ["Can't locate ExtUtils/Depends.pm in @INC (you may need to " "install the ExtUtils::Depends module) (@INC contains: " "/etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.32.1 " "/usr/local/share/perl/5.32.1 /usr/lib/x86_64-linux-gnu/perl5/5.32 " "/usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl-base " "/usr/lib/x86_64-linux-gnu/perl/5.32 " "/usr/share/perl/5.32 /usr/local/lib/site_perl) at " "(eval 11) line 1."], 1, MissingPerlModule( "ExtUtils/Depends.pm", "ExtUtils::Depends", [ "/etc/perl", "/usr/local/lib/x86_64-linux-gnu/perl/5.32.1", "/usr/local/share/perl/5.32.1", "/usr/lib/x86_64-linux-gnu/perl5/5.32", "/usr/share/perl5", "/usr/lib/x86_64-linux-gnu/perl-base", "/usr/lib/x86_64-linux-gnu/perl/5.32", "/usr/share/perl/5.32", "/usr/local/lib/site_perl"])) self.run_test( ["Pod::Weaver::Plugin::WikiDoc (for section -WikiDoc) " "does not appear to be installed"], 1, MissingPerlModule(None, "Pod::Weaver::Plugin::WikiDoc")) self.run_test( ["List::Util version 1.56 required--this is only version 1.55 " "at /build/tmpttq5hhpt/package/blib/lib/List/AllUtils.pm line 8."], 1, MissingPerlModule(None, "List::Util", minimum_version="1.56")) def test_missing_perl_file(self): self.run_test( [ "Can't locate debian/perldl.conf in @INC (@INC contains: " "/<>/inc /etc/perl /usr/local/lib/x86_64-linux-gnu" "/perl/5.28.1 /usr/local/share/perl/5.28.1 /usr/lib/" "x86_64-linux-gnu/perl5/5.28 /usr/share/perl5 " "/usr/lib/x86_64-linux-gnu/perl/5.28 /usr/share/perl/5.28 " "/usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base) " "at Makefile.PL line 131." ], 1, MissingPerlFile( "debian/perldl.conf", [ "/<>/inc", "/etc/perl", "/usr/local/lib/x86_64-linux-gnu/perl/5.28.1", "/usr/local/share/perl/5.28.1", "/usr/lib/x86_64-linux-gnu/perl5/5.28", "/usr/share/perl5", "/usr/lib/x86_64-linux-gnu/perl/5.28", "/usr/share/perl/5.28", "/usr/local/lib/site_perl", "/usr/lib/x86_64-linux-gnu/perl-base", ], ), ) self.run_test( ['Can\'t open perl script "Makefile.PL": No such file or directory'], 1, MissingPerlFile("Makefile.PL"), ) def test_missing_maven_artifacts(self): self.run_test( [ "[ERROR] Failed to execute goal on project byteman-bmunit5: Could " "not resolve dependencies for project " "org.jboss.byteman:byteman-bmunit5:jar:4.0.7: The following " "artifacts could not be resolved: " "org.junit.jupiter:junit-jupiter-api:jar:5.4.0, " "org.junit.jupiter:junit-jupiter-params:jar:5.4.0, " "org.junit.jupiter:junit-jupiter-engine:jar:5.4.0: " "Cannot access central (https://repo.maven.apache.org/maven2) " "in offline mode and the artifact " "org.junit.jupiter:junit-jupiter-api:jar:5.4.0 has not been " "downloaded from it before. -> [Help 1]" ], 1, MissingMavenArtifacts( [ "org.junit.jupiter:junit-jupiter-api:jar:5.4.0", "org.junit.jupiter:junit-jupiter-params:jar:5.4.0", "org.junit.jupiter:junit-jupiter-engine:jar:5.4.0", ] ), ) self.run_test( [ "[ERROR] Failed to execute goal on project opennlp-uima: Could " "not resolve dependencies for project " "org.apache.opennlp:opennlp-uima:jar:1.9.2-SNAPSHOT: Cannot " "access ApacheIncubatorRepository " "(http://people.apache.org/repo/m2-incubating-repository/) in " "offline mode and the artifact " "org.apache.opennlp:opennlp-tools:jar:debian has not been " "downloaded from it before. -> [Help 1]" ], 1, MissingMavenArtifacts(["org.apache.opennlp:opennlp-tools:jar:debian"]), ) self.run_test( [ "[ERROR] Failed to execute goal on project bookkeeper-server: " "Could not resolve dependencies for project " "org.apache.bookkeeper:bookkeeper-server:jar:4.4.0: Cannot " "access central (https://repo.maven.apache.org/maven2) in " "offline mode and the artifact io.netty:netty:jar:debian " "has not been downloaded from it before. -> [Help 1]" ], 1, MissingMavenArtifacts(["io.netty:netty:jar:debian"]), ) self.run_test( [ "[ERROR] Unresolveable build extension: Plugin " "org.apache.felix:maven-bundle-plugin:2.3.7 or one of its " "dependencies could not be resolved: Cannot access central " "(https://repo.maven.apache.org/maven2) in offline mode and " "the artifact org.apache.felix:maven-bundle-plugin:jar:2.3.7 " "has not been downloaded from it before. @" ], 1, MissingMavenArtifacts(["org.apache.felix:maven-bundle-plugin:2.3.7"]), ) self.run_test( [ "[ERROR] Plugin org.apache.maven.plugins:maven-jar-plugin:2.6 " "or one of its dependencies could not be resolved: Cannot access " "central (https://repo.maven.apache.org/maven2) in offline mode " "and the artifact " "org.apache.maven.plugins:maven-jar-plugin:jar:2.6 has not been " "downloaded from it before. -> [Help 1]" ], 1, MissingMavenArtifacts(["org.apache.maven.plugins:maven-jar-plugin:2.6"]), ) self.run_test( [ "[FATAL] Non-resolvable parent POM for " "org.joda:joda-convert:2.2.1: Cannot access central " "(https://repo.maven.apache.org/maven2) in offline mode " "and the artifact org.joda:joda-parent:pom:1.4.0 has not " "been downloaded from it before. and 'parent.relativePath' " "points at wrong local POM @ line 8, column 10" ], 1, MissingMavenArtifacts(["org.joda:joda-parent:pom:1.4.0"]), ) self.run_test( [ "[ivy:retrieve] \t\t:: " "com.carrotsearch.randomizedtesting#junit4-ant;" "${/com.carrotsearch.randomizedtesting/junit4-ant}: not found" ], 1, MissingMavenArtifacts( ["com.carrotsearch.randomizedtesting:junit4-ant:jar:debian"] ), ) def test_maven_errors(self): self.run_test( [ "[ERROR] Failed to execute goal " "org.apache.maven.plugins:maven-jar-plugin:3.1.2:jar " "(default-jar) on project xslthl: Execution default-jar of goal " "org.apache.maven.plugins:maven-jar-plugin:3.1.2:jar failed: " "An API incompatibility was encountered while executing " "org.apache.maven.plugins:maven-jar-plugin:3.1.2:jar: " "java.lang.NoSuchMethodError: " "'void org.codehaus.plexus.util.DirectoryScanner." "setFilenameComparator(java.util.Comparator)'" ], 1, None, ) def test_dh_missing_uninstalled(self): self.run_test( [ "dh_missing --fail-missing", "dh_missing: usr/share/man/man1/florence_applet.1 exists in " "debian/tmp but is not installed to anywhere", "dh_missing: usr/lib/x86_64-linux-gnu/libflorence-1.0.la exists " "in debian/tmp but is not installed to anywhere", "dh_missing: missing files, aborting", ], 3, DhMissingUninstalled("usr/lib/x86_64-linux-gnu/libflorence-1.0.la"), ) def test_dh_until_unsupported(self): self.run_test( [ "dh: The --until option is not supported any longer (#932537). " "Use override targets instead." ], 1, DhUntilUnsupported(), ) def test_missing_xml_entity(self): self.run_test( [ "I/O error : Attempt to load network entity " "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" ], 1, MissingXmlEntity("http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"), ) def test_ccache_error(self): self.run_test( [ "ccache: error: Failed to create directory " "/sbuild-nonexistent/.ccache/tmp: Permission denied" ], 1, CcacheError( "Failed to create directory " "/sbuild-nonexistent/.ccache/tmp: Permission denied" ), ) def test_dh_addon_load_failure(self): self.run_test( [ "dh: unable to load addon nodejs: " "Debian/Debhelper/Sequence/nodejs.pm did not return a true " "value at (eval 11) line 1." ], 1, DhAddonLoadFailure("nodejs", "Debian/Debhelper/Sequence/nodejs.pm"), ) def test_missing_library(self): self.run_test( ["/usr/bin/ld: cannot find -lpthreads"], 1, MissingLibrary("pthreads") ) self.run_test( [ "./testFortranCompiler.f:4: undefined reference to `sgemm_'", ], 1, ) self.run_test( [ "writer.d:59: error: undefined reference to 'sam_hdr_parse_'", ], 1, ) def test_fpic(self): self.run_test( [ "/usr/bin/ld: pcap-linux.o: relocation R_X86_64_PC32 against " "symbol `stderr@@GLIBC_2.2.5' can not be used when making a " "shared object; recompile with -fPIC" ], 1, None, ) def test_rspec(self): self.run_test( [ "rspec ./spec/acceptance/cookbook_resource_spec.rb:20 " "# Client API operations downloading a cookbook when the " "cookbook of the name/version is found downloads the " "cookbook to the destination" ], 1, None, ) def test_multiple_definition(self): self.run_test( [ "./dconf-paths.c:249: multiple definition of " "`dconf_is_rel_dir'; client/libdconf-client.a(dconf-paths.c.o):" "./obj-x86_64-linux-gnu/../common/dconf-paths.c:249: " "first defined here" ], 1, ) self.run_test( [ "/usr/bin/ld: ../lib/libaxe.a(stream.c.o):(.bss+0x10): " "multiple definition of `gsl_message_mask'; " "../lib/libaxe.a(error.c.o):(.bss+0x8): first defined here" ], 1, ) def test_missing_ruby_gem(self): self.run_test( [ "Could not find gem 'childprocess (~> 0.5)', which is " "required by gem 'selenium-webdriver', in any of the sources." ], 1, MissingRubyGem("childprocess", "0.5"), ) self.run_test( [ "Could not find gem 'rexml', which is required by gem " "'rubocop', in any of the sources." ], 1, MissingRubyGem("rexml"), ) self.run_test( [ "/usr/lib/ruby/2.5.0/rubygems/dependency.rb:310:in `to_specs': " "Could not find 'http-parser' (~> 1.2.0) among 59 total gem(s) " "(Gem::MissingSpecError)" ], 1, MissingRubyGem("http-parser", "1.2.0"), ) self.run_test( [ "/usr/lib/ruby/2.5.0/rubygems/dependency.rb:312:in `to_specs': " "Could not find 'celluloid' (~> 0.17.3) - did find: " "[celluloid-0.16.0] (Gem::MissingSpecVersionError)" ], 1, MissingRubyGem("celluloid", "0.17.3"), ) self.run_test( [ "/usr/lib/ruby/2.5.0/rubygems/dependency.rb:312:in `to_specs': " "Could not find 'i18n' (~> 0.7) - did find: [i18n-1.5.3] " "(Gem::MissingSpecVersionError)" ], 1, MissingRubyGem("i18n", "0.7"), ) self.run_test( [ "/usr/lib/ruby/2.5.0/rubygems/dependency.rb:310:in `to_specs': " "Could not find 'sassc' (>= 2.0.0) among 34 total gem(s) " "(Gem::MissingSpecError)" ], 1, MissingRubyGem("sassc", "2.0.0"), ) self.run_test( [ "/usr/lib/ruby/2.7.0/bundler/resolver.rb:290:in " "`block in verify_gemfile_dependencies_are_found!': " "Could not find gem 'rake-compiler' in any of the gem sources " "listed in your Gemfile. (Bundler::GemNotFound)" ], 1, MissingRubyGem("rake-compiler"), ) self.run_test( [ "/usr/lib/ruby/2.7.0/rubygems.rb:275:in `find_spec_for_exe': " "can't find gem rdoc (>= 0.a) with executable rdoc " "(Gem::GemNotFoundException)" ], 1, MissingRubyGem("rdoc", "0.a"), ) def test_missing_php_class(self): self.run_test( [ "PHP Fatal error: Uncaught Error: Class " "'PHPUnit_Framework_TestCase' not found in " "/tmp/autopkgtest.gO7h1t/build.b1p/src/Horde_Text_Diff-" "2.2.0/test/Horde/Text/Diff/EngineTest.php:9" ], 1, MissingPhpClass("PHPUnit_Framework_TestCase"), ) def test_missing_java_class(self): self.run_test( """\ Caused by: java.lang.ClassNotFoundException: org.codehaus.Xpp3r$Builder \tat org.codehaus.strategy.SelfFirstStrategy.loadClass(lfFirstStrategy.java:50) \tat org.codehaus.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271) \tat org.codehaus.realm.ClassRealm.loadClass(ClassRealm.java:247) \tat org.codehaus.realm.ClassRealm.loadClass(ClassRealm.java:239) \t... 46 more """.splitlines(), 1, MissingJavaClass("org.codehaus.Xpp3r$Builder"), ) def test_install_docs_link(self): self.run_test( """\ dh_installdocs: --link-doc not allowed between sympow and sympow-data (one is \ arch:all and the other not)""".splitlines(), 1, ) def test_r_missing(self): self.run_test( [ "ERROR: dependencies ‘ellipsis’, ‘pkgload’ are not available " "for package ‘testthat’" ], 1, MissingRPackage("ellipsis"), ) self.run_test( [ " namespace ‘DBI’ 1.0.0 is being loaded, " "but >= 1.0.0.9003 is required" ], 1, MissingRPackage("DBI", "1.0.0.9003"), ) self.run_test( [ " namespace ‘spatstat.utils’ 1.13-0 is already loaded, " "but >= 1.15.0 is required" ], 1, MissingRPackage("spatstat.utils", "1.15.0"), ) self.run_test( [ "Error in library(zeligverse) : there is no package called " "'zeligverse'" ], 1, MissingRPackage("zeligverse"), ) self.run_test( ["there is no package called 'mockr'"], 1, MissingRPackage("mockr") ) self.run_test( [ "ERROR: dependencies 'igraph', 'matlab', 'expm', 'RcppParallel' are not available for package 'markovchain'" ], 1, MissingRPackage("igraph"), ) self.run_test( [ "Error: package 'BH' 1.66.0-1 was found, but >= 1.75.0.0 is required by 'RSQLite'" ], 1, MissingRPackage("BH", "1.75.0.0"), ) self.run_test( [ "Error: package ‘AnnotationDbi’ 1.52.0 was found, but >= 1.53.1 is required by ‘GO.db’" ], 1, MissingRPackage("AnnotationDbi", "1.53.1")) self.run_test( [" namespace 'alakazam' 1.1.0 is being loaded, but >= 1.1.0.999 is required"], 1, MissingRPackage('alakazam', '1.1.0.999')) def test_mv_stat(self): self.run_test( ["mv: cannot stat '/usr/res/boss.png': No such file or directory"], 1, MissingFile("/usr/res/boss.png"), ) self.run_test(["mv: cannot stat 'res/boss.png': No such file or directory"], 1) def test_dh_link_error(self): self.run_test( [ "dh_link: link destination debian/r-cran-crosstalk/usr/lib/R/" "site-library/crosstalk/lib/ionrangeslider is a directory" ], 1, DhLinkDestinationIsDirectory( "debian/r-cran-crosstalk/usr/lib/R/site-library/crosstalk/" "lib/ionrangeslider" ), ) def test_go_test(self): self.run_test( ["FAIL\tgithub.com/edsrzf/mmap-go\t0.083s"], 1, None, ) def test_debhelper_pattern(self): self.run_test( [ "dh_install: Cannot find (any matches for) " '"server/etc/gnumed/gnumed-restore.conf" ' "(tried in ., debian/tmp)" ], 1, DebhelperPatternNotFound( "server/etc/gnumed/gnumed-restore.conf", "install", [".", "debian/tmp"] ), ) def test_symbols(self): self.run_test( [ "dpkg-gensymbols: error: some symbols or patterns disappeared in " "the symbols file: see diff output below" ], 1, DisappearedSymbols(), ) def test_autoconf_macro(self): self.run_test( ["configure.in:1802: error: possibly undefined macro: " "AC_CHECK_CCA"], 1, MissingAutoconfMacro("AC_CHECK_CCA"), ) self.run_test( ["./configure: line 12569: PKG_PROG_PKG_CONFIG: command not found"], 1, MissingAutoconfMacro("PKG_PROG_PKG_CONFIG"), ) self.run_test( [ "checking for gawk... (cached) mawk", "./configure: line 2368: syntax error near unexpected token `APERTIUM,'", "./configure: line 2368: `PKG_CHECK_MODULES(APERTIUM, apertium >= 3.7.1)'", ], 3, MissingAutoconfMacro("PKG_CHECK_MODULES", need_rebuild=True), ) self.run_test( [ "checking for libexif to use... ./configure: line 15968: syntax error near unexpected token `LIBEXIF,libexif'", "./configure: line 15968: `\t\t\t\t\t\tPKG_CHECK_MODULES(LIBEXIF,libexif >= 0.6.18,have_LIBEXIF=yes,:)'", ], 2, MissingAutoconfMacro("PKG_CHECK_MODULES", need_rebuild=True)) def test_config_status_input(self): self.run_test( ["config.status: error: cannot find input file: " "`po/Makefile.in.in'"], 1, MissingConfigStatusInput("po/Makefile.in.in"), ) def test_jvm(self): self.run_test( [ "ERROR: JAVA_HOME is set to an invalid " "directory: /usr/lib/jvm/default-java/" ], 1, MissingJVM(), ) def test_cp(self): self.run_test( [ "cp: cannot stat " "'/<>/debian/patches/lshw-gtk.desktop': " "No such file or directory" ], 1, None, ) def test_automake_input(self): self.run_test( [ "automake: error: cannot open < gtk-doc.make: " "No such file or directory" ], 1, MissingAutomakeInput("gtk-doc.make"), ) def test_shellcheck(self): self.run_test( [ " " * 40 + "^----^ SC2086: " "Double quote to prevent globbing and word splitting." ], 1, None, ) buildlog-consultant_0.0.18.orig/buildlog_consultant/tests/test_sbuild.py0000644000000000000000000000411214034351606023650 0ustar00#!/usr/bin/python # Copyright (C) 2019-2021 Jelmer Vernooij # # 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 import unittest from ..sbuild import ( find_brz_build_error, parse_brz_error, InconsistentSourceFormat, MissingDebcargoCrate, ) class ParseBrzErrorTests(unittest.TestCase): def test_inconsistent_source_format(self): self.assertEqual( ( InconsistentSourceFormat(), "Inconsistent source format between version and source " "format", ), parse_brz_error( "Inconsistency between source format and version: version " "is not native, format is native.", [], ), ) class FindBrzBuildErrorTests(unittest.TestCase): def test_missing_debcargo_crate(self): lines = [ "Using crate name: version-check, version 0.9.2", " Updating crates.io index\n", "\x1b[1;31mSomething failed: Couldn't find any crate " "matching version-check = 0.9.2\n", " Try `debcargo update` to update the crates.io index.\x1b[0m\n", "brz: ERROR: Debcargo failed to run.\n", ] err, line = find_brz_build_error(lines) self.assertEqual( line, "debcargo can't find crate version-check (version: 0.9.2)" ) self.assertEqual(err, MissingDebcargoCrate("version-check", "0.9.2"))