[opkg][PATCH 00/16] Update/Restructure developer documentation

22 views
Skip to first unread message

Alex Stewart

unread,
Jan 17, 2023, 11:37:57 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The developer documentation for our opkg fork is very out of date, and
doesn't do a good job communicating to new developers how to build and
test their changes. Further, the project layout is in the old GNU style,
which is an unnecessary deviation from what most modern developers
expect.

In short, this patchset:
* Updates our version and release policies to reflect the realities of
how this project has been operating for years now.
* Moves developer documentation into a `:docs/` directory.
* Adds modern CONTRIBUTING and MAINTAINING files with useful information
for devs/maintainers.
* Clarifies where the correct project CHANGELOG is recorded.
* Removes obsolescent GNU docs files (like AUTHORS, TODO, et c.).
* Updates the top level README.
* Adds a `:tests/README` file to help devs author and run tests.
* Removes the obsolescent checkpatch.pl and test-branch.py scripts.

This patchset doesn't intentionally change any opkg behavior, or how the
project is functionally built.

I'll leave this patchset up for a week or so to give those who are
interested time to look over these changes and give feedback.

Alex Stewart (17):
CONTRIBUTING: reworked
AUTHORS: deprecate
ChangeLog.ipkg: move to docs/
ChangeLog: remove
NEWS: rename to CHANGELOG.md
INSTALL: remove
VersionPoilcy: move to MAINTAINING.md
RELEASE: move to MAINTAINING and update
GettingStarted: remove
README: improve and enhance
configure.ac: declare the project as "foreign"
Makefile.am: cleanup dist files
tests: add README
gitignore: ignore configure.lineno
TODO: remove
ROADMAP: add an initial roadmap
remove obsolete scripts

.gitignore | 2 +
AUTHORS | 50 -
NEWS => CHANGELOG.md | 6 +-
CONTRIBUTING | 183 --
ChangeLog | 1 -
INSTALL | 201 --
Makefile.am | 9 +-
README | 13 -
README.md | 50 +
TODO | 49 -
configure.ac | 2 +-
developer-doc/GettingStarted | 51 -
developer-doc/Release | 42 -
developer-doc/VersionPolicy | 21 -
ChangeLog.ipkg => docs/CHANGELOG.ipkg | 0
docs/CONTRIBUTING.md | 161 ++
docs/MAINTAINING.md | 98 +
docs/ROADMAP.md | 51 +
scripts/checkpatch.pl | 3730 -------------------------
scripts/test-branch.py | 84 -
tests/README.md | 91 +
21 files changed, 465 insertions(+), 4430 deletions(-)
delete mode 100644 AUTHORS
rename NEWS => CHANGELOG.md (99%)
delete mode 100644 CONTRIBUTING
delete mode 100644 ChangeLog
delete mode 100644 INSTALL
delete mode 100644 README
create mode 100644 README.md
delete mode 100644 TODO
delete mode 100644 developer-doc/GettingStarted
delete mode 100644 developer-doc/Release
delete mode 100644 developer-doc/VersionPolicy
rename ChangeLog.ipkg => docs/CHANGELOG.ipkg (100%)
create mode 100644 docs/CONTRIBUTING.md
create mode 100644 docs/MAINTAINING.md
create mode 100644 docs/ROADMAP.md
delete mode 100755 scripts/checkpatch.pl
delete mode 100755 scripts/test-branch.py
create mode 100644 tests/README.md

--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:00 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The CONTRIBUTING file contents is out of date and doesn't give new
developers a helpful starting point for contributing to the project.

* Move the document to a new :docs/ directory, which will be the new
home for developer documentation.
* Rewrite sections to reflect the current standards for project
contribution.
* Remove sections about how to use git; most devs probably understand
how to use it by now.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
CONTRIBUTING | 183 -------------------------------------------
docs/CONTRIBUTING.md | 161 +++++++++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+), 183 deletions(-)
delete mode 100644 CONTRIBUTING
create mode 100644 docs/CONTRIBUTING.md

diff --git a/CONTRIBUTING b/CONTRIBUTING
deleted file mode 100644
index 17ebe54..0000000
--- a/CONTRIBUTING
+++ /dev/null
@@ -1,183 +0,0 @@
-% Contributing to opkg
-
-## Starting off
-
-To contribute to opkg, you'll need a basic understanding of how to use git.
-You'll also need to make sure you can use `git send-email` - maybe try sending a
-few patches to your own email address first to check you've got it set up
-correctly.
-
-If you don't have the latest opkg sources, you'll need to clone the official
-repository:
-
- git clone git://git.yoctoproject.org/opkg.git
-
-## Contributing bugfixes to a release branch
-
-Currently, all new feature development takes place on the master branch and we
-have one maintained release branch (currently opkg-0.2.x). This simple branching
-strategy might need to change once we reach version 1.0 but it should work for
-now.
-
-If you're developing new features or making backwards incompatible changes, base
-your work on the master branch.
-
-If your patch fixes a bug in a released version of opkg, it would be good to
-base it off the maintained release branch rather than to the master branch. That
-way it can be included in a bugfix release. To do this, checkout the release
-branch before developing your fix and ensure the patch email is sent with a
-prefix indicating which branch it applies to. The following commands would do
-this for the opkg-0.2.x branch:
-
- git checkout opkg-0.2.x
- git config format.subjectprefix "opkg-0.2.x PATCH"
-
-## Developing patches
-
-The make and commit your changes (that bit is left as an exercize for the
-reader). Developing on a separate branch is a very good idea:
-
- git checkout -b my-branch
- git add ... # As appropriate
- git commit -s # As appropriate
-
-Adding a "Signed-off-by" line (using the -s option to `git commit`) is very
-important and signifies acceptance of the Developer's Certificate of Origin (see
-appendix). We also accept "Cc", "Acked-by" and "Tested-by" lines as appropriate,
-as per the [submission guidelines for the Linux kernel][1]. We're not currently
-interested in "Reviewed-by", "Reported-by" or "Suggested-by" lines.
-
-The subject (first line of the commit message) for each patch should begin with
-the submodule, file or function that is modified, followed by a short (<50
-characters if possible) description of the change. That helps
-a lot to find anything related to a submodule or file by searching with a
-simple 'git log | grep [foo]'.
-
-Examples of good subjects:
-
- libopkg: changing searching order for packages
- opkg_conf_init: adding new field 'short_desc'
- src/opkg.c: fixed return value in function foo
-
-The remainder of the commit message should explain in detail what was changed
-and why.
-
-Each patch should make a single logical change. If multiple files are changed to
-fix one bug or add one feature, that should be a single patch. If one file is
-changed to fix multiple bugs, that should be multiple patches.
-
-[1]: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/SubmittingPatches
-
-## Testing
-
-Assuming you have python (version 3 or later) installed on your system, you can
-test your patches by running the 'test-branch.py' script in the 'tests'
-directory:
-
- python3 ./tests/test-branch.py
-
-By default, this compiles and runs the test suite for each patch between
-'master' (which should be the latest upstream code) and 'HEAD' (which will be
-your latest commit). If your patches apply to a different branch (eg.
-opkg-0.2.x), you need to set the OPKG_MASTER environment variable when you run
-the script:
-
- OPKG_MASTER=opkg-0.2.x python3 ./tests/test-branch.py
-
-## Submitting patches for review
-
-Please submit all patches to the opkg mailing list opkg-...@googlegroups.com.
-Also make sure that you use the -M option on the command line or the
-"diff.renames=copy" option in your config.
-
-That sounds like a lot to remember but if you run the following commands in your
-opkg directory it will all be done automatically when you use `git format-patch`
-or `git send-email`:
-
- git config format.to "opkg-...@googlegroups.com"
- git config diff.renames copy
-
-To prepare your patches, assuming you took the advice before about developing on
-a separate branch:
-
- git format-patch master
-
-You can add `--cover-letter` to that command if you want to include a
-descriptive email along with your patches.
-
-Before submitting your patches, please ensure that they fit the coding
-style which is used within the project. To assist with this you can run the
-checkpatch.pl script (in scripts) which is able to pinpoint various style
-violations on both patches and source code. However, please bear in mind that
-checkpatch.pl is not perfect and it may sometimes give bad advice. To quote Ted
-Ts'o on LKML: You need to know when the right thing to do is to say, "Go home,
-checkpatch, you're drunk."
-
-To use this script, simply invoke it from the command line. If you want to
-check a whole file, use the following invocation:
-
- ./scripts/checkpatch.pl -f {yourfile}
-
-To check a patch generated by the command lines above, use the following
-invocation:
-
- ./scripts/checkpatch.pl {yourfile}
-
-Correct any error and warning that are reported by the tool. If you think
-the tool reported a false positive, please report a bug on the [opkg-devel
-mailing list][1].
-
-The additional use of option --ignore CODE_INDENT is encouraged for a
-while if you experience some issue related to code indentation. This is
-very likely to change in the future as the code base will be streamlined
-to use fixed indentation rules.
-
-Once you've checked the generated patch files, run:
-
- git send-email
-
-That should result in your patches being sent to the relevant mailing lists in
-the correct format. The patches should then be reviewed and you should receive
-feedback by email. If you haven't heard anything within 2 weeks, feel free to
-send us a reminder.
-
-If you need any further help or advice, just ask on the [opkg-devel mailing
-list][1].
-
-[1]: mailto:opkg-...@googlegroups.com
-
-## Commit Access
-
-If you have commit access to the opkg git repository, it is assumed that you
-know what you're doing. You can bypass the mailing list and push your changes
-directly to the public repository, just make sure that you test things properly
-and sign off your patches!
-
-Appendix: Developer's Certificate of Origin
-===========================================
-
- Developer's Certificate of Origin 1.1
-
- By making a contribution to this project, I certify that:
-
- (a) The contribution was created in whole or in part by me and I
- have the right to submit it under the open source license
- indicated in the file; or
-
- (b) The contribution is based upon previous work that, to the best
- of my knowledge, is covered under an appropriate open source
- license and I have the right under that license to submit that
- work with modifications, whether created in whole or in part
- by me, under the same open source license (unless I am
- permitted to submit under a different license), as indicated
- in the file; or
-
- (c) The contribution was provided directly to me by some other
- person who certified (a), (b) or (c) and I have not modified
- it.
-
- (d) I understand and agree that this project and the contribution
- are public and that a record of the contribution (including all
- personal information I submit with it, including my sign-off) is
- maintained indefinitely and may be redistributed consistent with
- this project or the open source license(s) involved.
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 0000000..706005f
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,161 @@
+# Contributing to Opkg
+
+Thanks for taking an interest in contributing to the [Yocto project's](https://www.yoctoproject.org/) fork of the Opkg package manager!
+
+This document should provide useful information for how to get started developing, testing, and upstreaming to our mainline.
+
+
+----
+## Communication
+
+Clone the **source** from the Yocto project's [git server](https://git.yoctoproject.org/opkg/), and read the [README](https://git.yoctoproject.org/opkg/tree/README).
+
+Get **help** using this repository and **discuss** changes on the [opkg-devel](https://groups.google.com/g/opkg-devel) google group mailing list.
+
+File **bugs** and **enhancement** requests to the Yocto project's [opkg bugzilla tracker](https://bugzilla.yoctoproject.org/buglist.cgi?quicksearch=Product%3Aopkg)
+
+Send **security** concerns directly to the project **maintainer** by email: Alex Stewart <[alex.s...@ni.com](mailto:alex.s...@ni.com)>
+
+**Historic Maintainers**
+- Alejandro del Castillo <alejandro....@ni.com>
+- Paul Barker <pa...@paulbarker.me.uk>
+- Thomas Wood <tho...@openedhand.com>
+- Tick Chen <tickt...@gmail.com>
+- Graham Gower <graham...@gmail.com>
+- Ipkg
+ - Pierluigi Frullani <pi...@frumar.it>
+ - Carl Worth <cwo...@handhelds.org>
+ - Steve Ayer <steve...@compaq.com>
+
+
+----
+## Building Opkg
+
+1. Build the libsolv satisfaction solver.
+
+[libsolv](https://github.com/openSUSE/libsolv) is a free package dependency solver, which opkg uses as its backend satisfaction solver. Though it is strictly possible to build and use opkg with its legacy "internal" solver instead, it is not well-maintained and not recommended.
+
+You will need the libsolv development headers to build opkg. If they are not available from your distribution vendor, you can quickly build and install libsolv using the following commands on debian/ubuntu.
+
+```bash
+apt-get update && apt-get install cmake g++ git zlib1g-dev
+git clone https://github.com/openSUSE/libsolv.git /usr/local/libsolv
+cd /usr/local/libsolv
+mkdir build && cd build
+cmake \
+ -DCMAKE_INSTALL_PREFIX="/usr/local/" \
+ -DENABLE_COMPLEX_DEPS=on \
+ -DMULTI_SEMANTICS=on \
+ ..
+make install
+ldconfig
+```
+
+**NOTE:** The libsolv `ENABLE_COMPLEX_DEPS` and `MULTI_SEMANTICS` options must be enabled for libsolv to support opkg.
+
+2. Install opkg build requirements.
+
+**Debian 11**
+```bash
+apt-get update && apt-get install -y autoconf automake git libarchive-dev libcurl4-openssl-dev libgpgme11-dev libssl-dev libtool-bin make pkg-config python3 shtool
+```
+
+3. Clone the repository from the Yocto project git server. The use autoconf to configure, and gnu make to build the project.
+
+```bash
+git clone https://git.yoctoproject.org/opkg --branch master
+cd opkg
+autoconf
+./configure --with-libsolv
+make
+```
+
+4. (Optional) Install opkg to your system.
+
+It is not required that you install opkg to test your changes, but you can do so with the following steps.
+
+```bash
+./configure --with-libsolv --prefix=/usr/local
+# or some other installation --prefix
+make install
+# verify:
+opkg --version
+```
+
+
+----
+## Testing
+
+Once you have *built* your change, you can validate your changes using the integrated opkg test suite, in the `:tests/` directory.
+
+The simplest way to run the tests is using the check make-target (`make check`). This target will compile your changes (if necessary), and then run the whole suite of functional and regression tests against the compiled opkg binary.
+
+Some regression tests are expected to fail when using either the internal satisfaction solver or libsolv; they will display as such when run. Please ensure that all tests are passing before submitting your changes to the mailing list.
+
+
+----
+## Commit Guidelines
+
+This project uses *commits* as the fundamental unit-of-change (not pull requests). So please follow common best practices for authoring your commits such that they are: atomic, comprehensible, and considerate of users who work in the embedded space.
+
+**Commit Signing-offs.** Please add a signoff to each of your commits, using the `--signoff` argument to git. (eg. `git commit -s`)
+
+**Bug Fixes.** If your change resolves a bug from the opkg bugzilla, please include a `Closes: ${bugzilla number}` trailer to your commit message.
+
+```bash
+git commit -s --trailer Closes=12345
+```
+
+
+----
+## Submitting Changes Upstream
+
+**Git Send-Email.** Once you have *tested* your change, you can submit it to the opkg mainline by embedding it into an email, and sending it to the opkg-devel mailing list (<opkg-...@googlegroups.com>). When you do, please prefix your email with the tags: `[opkg][PATCH]`.
+
+The easiest way to do this is using the git send-email extension. You can use the following commands to configure your opkg workspace with the correct defaults.
+
+```bash
+git config diff.renames copy
+git config format.to "opkg-...@googlegroups.com"
+git config format.subjectprefix "opkg][PATCH"
+```
+
+You can then create a patchset using the `git format-patch` command, and send it upstream. eg.
+
+```bash
+git format-patch origin/master..HEAD
+# or
+git format-patch --cover-letter origin/master..HEAD
+
+git send-email ./*.patch
+```
+
+
+----
+## Developer's Certificate of Origin
+
+Developer's Certificate of Origin 1.1
+
+By making a contribution to this project, I certify that:
+
+(a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source license
+ indicated in the file; or
+
+(b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ license and I have the right under that license to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source license (unless I am
+ permitted to submit under a different license), as indicated
+ in the file; or
+
+(c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+(d) I understand and agree that this project and the contribution
+ are public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source license(s) involved.
\ No newline at end of file
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:05 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The AUTHORS file lists some historic project contributors/maintainers,
but is rather out of date. Further, I think the open source community
has largely moved away from keeping independent AUTHORS files, in favor
of allowing the VCS history to track that information.

For those reasons deprecate the AUTHORS file. The maintainers list has
already been moved to the docs/CONTRIBUTING.md document, since that
information is less-well tracked by git history and has some utility for
contributors.

Hopefully, no one feels slighted by this decision. If you really desire
to have your contributions written out in source control, or if you had
some copyright agreement which makes that a requirement, feel free to
let me know and I'll reconcile your request.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
AUTHORS | 50 --------------------------------------------------
1 file changed, 50 deletions(-)
delete mode 100644 AUTHORS

diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 96a44d4..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,50 +0,0 @@
-% Opkg Authors and Maintainers
-
-## Current Maintainer
-
- * Alejandro del Castillo <alejandro....@ni.com>
-
-## Previous Authors & Maintainers
-
-Opkg:
-
- * Thomas Wood <tho...@openedhand.com>
- * Tick Chen <tickt...@gmail.com>
- * Graham Gower <graham...@gmail.com>
- * Paul Barker <pa...@paulbarker.me.uk>
-
-Ipkg:
-
- * Pierluigi Frullani <pi...@frumar.it>
- * Carl Worth <cwo...@handhelds.org>
- * Steve Ayer <steve...@compaq.com>
-
-## Additional Contributors
-
-The following people have submitted changes which have been applied to the core:
-
- * Christopher Hall <h...@openmoko.com>
- * EdorFaus <edor...@gmail.com>
- * Graham Gower <graham...@gmail.com>
- * Krzysztof Kotlenga <po...@users.sf.net>
- * Per Hansen <spam...@yahoo.de>
- * Mike Westerhof <mwe...@dls.net>
- * Antonio Ospite <osp...@studenti.unina.it>
- * Koen Kooi <ko...@beagleboard.org>
- * Claudio Mignanti <claud...@gmail.com>
- * Florian Boor <floria...@kernelconcepts.de>
- * Camille Moncelier <monc...@devlife.org>
- * Jim Huang <jser...@gmail.com>
- * John L. Chmielewski <jlc...@gmail.com>
-
-We don't have your name / email:
-
- * Gilles Doffe
- * cconroy
- * chgros
- * Kosmaty
- * manitu
- * pbla...@gmail.com
-
-And anyone else who has sent patches or bug reports. If your name isn't on this
-list, please forgive us and send an email to a maintainer.
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:10 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
Move the very-historic Ipkg changelog to the docs/ directory. It is
unlikely to be useful to new developers, and doesn't deserve
top-directory prominence.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
ChangeLog.ipkg => docs/CHANGELOG.ipkg | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename ChangeLog.ipkg => docs/CHANGELOG.ipkg (100%)

diff --git a/ChangeLog.ipkg b/docs/CHANGELOG.ipkg
similarity index 100%
rename from ChangeLog.ipkg
rename to docs/CHANGELOG.ipkg
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:17 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The ChangeLog file just redirects the user to the git-log, which is
unhelpful. Remove it for cleanliness.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
ChangeLog | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 ChangeLog

diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index 5daf1f3..0000000
--- a/ChangeLog
+++ /dev/null
@@ -1 +0,0 @@
-Please see git log :/
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:21 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The NEWS file now represents as a modern-style markdown CHANGELOG, but
uses the old-style "NEWS" name.

Rename it to give the project a more *modern* appearance.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
NEWS => CHANGELOG.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
rename NEWS => CHANGELOG.md (99%)

diff --git a/NEWS b/CHANGELOG.md
similarity index 99%
rename from NEWS
rename to CHANGELOG.md
index 156bd0a..c4ef2f5 100644
--- a/NEWS
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
-# Changelog
+# Opkg Changelog

-This NEWS file for the opkg project attempts to conform to the [KeepAChangelog](https://keepachangelog.com/en/1.0.0/) standard.
+This changelog for the opkg project attempts to conform to the [KeepAChangelog](https://keepachangelog.com/en/1.0.0/) standard.
+
+For a historic list of changes from when the project was called "Ipkg", see the [`:docs/CHANGELOG.ipkg`](./docs/CHANGELOG.ipkg) file.


## opkg-0.6.1
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:26 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The INSTALL file is auto-generated by the automake tooling, and should
not be a part of source control.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
.gitignore | 1 +
INSTALL | 201 -------------------------------------------
docs/CONTRIBUTING.md | 2 +
3 files changed, 3 insertions(+), 201 deletions(-)
delete mode 100644 INSTALL

diff --git a/.gitignore b/.gitignore
index aadc1fb..44199f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ config.guess
config.log
config.status
config.sub
+INSTALL

# Auto-generated config header and timestamp
config.h
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index fede44c..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,201 +0,0 @@
-opkg uses autoconf and friends for configuration. The familiar steps of:
-
- ./configure [options]
- make
- make install
-
-should be sufficient if you downloaded an official release of opkg. If you
-obtained opkg from a Git or Subversion repository, you may need to run
-autogen.sh before running configure, as follows.
-
- [ACLOCAL_FLAGS=foo] ./autogen.sh
-
-The 'ACLOCAL_FLAGS' variable is optional.
-
-The remainder of this document is the standard INSTALL document
-provided by autoconf.
-
-- Opkg maintainers
-
-Basic Installation
-==================
-
- These are generic installation instructions.
-
- The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation. It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions. Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, a file
-`config.cache' that saves the results of its tests to speed up
-reconfiguring, and a file `config.log' containing compiler output
-(useful mainly for debugging `configure').
-
- If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release. If at some point `config.cache'
-contains results you don't want to keep, you may remove or edit it.
-
- The file `configure.in' is used to create `configure' by a program
-called `autoconf'. You only need `configure.in' if you want to change
-it or regenerate `configure' using a newer version of `autoconf'.
-
-The simplest way to compile this package is:
-
- 1. `cd' to the directory containing the package's source code and type
- `./configure' to configure the package for your system. If you're
- using `csh' on an old version of System V, you might need to type
- `sh ./configure' instead to prevent `csh' from trying to execute
- `configure' itself.
-
- Running `configure' takes awhile. While running, it prints some
- messages telling which features it is checking for.
-
- 2. Type `make' to compile the package.
-
- 3. Optionally, type `make check' to run any self-tests that come with
- the package.
-
- 4. Type `make install' to install the programs and any data files and
- documentation.
-
- 5. You can remove the program binaries and object files from the
- source code directory by typing `make clean'. To also remove the
- files that `configure' created (so you can compile the package for
- a different kind of computer), type `make distclean'. There is
- also a `make maintainer-clean' target, but that is intended mainly
- for the package's developers. If you use it, you may have to get
- all sorts of other programs in order to regenerate files that came
- with the distribution.
-
-Compilers and Options
-=====================
-
- Some systems require unusual options for compilation or linking that
-the `configure' script does not know about. You can give `configure'
-initial values for variables by setting them in the environment. Using
-a Bourne-compatible shell, you can do that on the command line like
-this:
- CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
-
-Or on systems that have the `env' program, you can do it like this:
- env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
-
-Compiling For Multiple Architectures
-====================================
-
- You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory. To do this, you must use a version of `make' that
-supports the `VPATH' variable, such as GNU `make'. `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script. `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
-
- If you have to use a `make' that does not supports the `VPATH'
-variable, you have to compile the package for one architecture at a time
-in the source code directory. After you have installed the package for
-one architecture, use `make distclean' before reconfiguring for another
-architecture.
-
-Installation Names
-==================
-
- By default, `make install' will install the package's files in
-`/usr/local/bin', `/usr/local/man', etc. You can specify an
-installation prefix other than `/usr/local' by giving `configure' the
-option `--prefix=PATH'.
-
- You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files. If you
-give `configure' the option `--exec-prefix=PATH', the package will use
-PATH as the prefix for installing programs and libraries.
-Documentation and other data files will still use the regular prefix.
-
- In addition, if you use an unusual directory layout you can give
-options like `--bindir=PATH' to specify different values for particular
-kinds of files. Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
-
- If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-Optional Features
-=================
-
- Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System). The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
- For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-Specifying the System Type
-==========================
-
- There may be some features `configure' can not figure out
-automatically, but needs to determine by the type of host the package
-will run on. Usually `configure' can figure that out, but if it prints
-a message saying it can not guess the host type, give it the
-`--host=TYPE' option. TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name with three fields:
- CPU-COMPANY-SYSTEM
-
-See the file `config.sub' for the possible values of each field. If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the host type.
-
- If you are building compiler tools for cross-compiling, you can also
-use the `--target=TYPE' option to select the type of system they will
-produce code for and the `--build=TYPE' option to select the type of
-system on which you are compiling the package.
-
-Sharing Defaults
-================
-
- If you want to set default values for `configure' scripts to share,
-you can create a site shell script called `config.site' that gives
-default values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists. Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Operation Controls
-==================
-
- `configure' recognizes the following options to control how it
-operates.
-
-`--cache-file=FILE'
- Use and save the results of the tests in FILE instead of
- `./config.cache'. Set FILE to `/dev/null' to disable caching, for
- debugging `configure'.
-
-`--help'
- Print a summary of the options to `configure', and exit.
-
-`--quiet'
-`--silent'
-`-q'
- Do not print messages saying which checks are being made. To
- suppress all normal output, redirect it to `/dev/null' (any error
- messages will still be shown).
-
-`--srcdir=DIR'
- Look for the package's source code in directory DIR. Usually
- `configure' can determine that directory automatically.
-
-`--version'
- Print the version of Autoconf used to generate the `configure'
- script, and exit.
-
-`configure' also accepts some other, not widely useful, options.
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 706005f..9b9eeb6 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -70,6 +70,8 @@ autoconf
make
```

+For more information about configuring projects with autoconf, please reference the [autoconf documentation](https://www.gnu.org/software/autoconf/manual/autoconf-2.71/html_node/Basic-Installation.html).
+
4. (Optional) Install opkg to your system.

It is not required that you install opkg to test your changes, but you can do so with the following steps.
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:30 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The VersionPolicy doc describes an approximation of the semantic
versioning policy that this fork follows.

The policy is functionally similar to the popular SemVer spec outlined
here (https://semver.org/spec/v2.0.0.html). So we should formally adopt
that spec to align with community best-practices.

There isn't a compelling reason that the version policy needs to be in
its own file. Create a new MAINTAINING.md document for project
maintenance information and move it there.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
developer-doc/VersionPolicy | 21 ---------------------
docs/MAINTAINING.md | 21 +++++++++++++++++++++
2 files changed, 21 insertions(+), 21 deletions(-)
delete mode 100644 developer-doc/VersionPolicy
create mode 100644 docs/MAINTAINING.md

diff --git a/developer-doc/VersionPolicy b/developer-doc/VersionPolicy
deleted file mode 100644
index 92455d1..0000000
--- a/developer-doc/VersionPolicy
+++ /dev/null
@@ -1,21 +0,0 @@
-opkg Version Policy
-===================
-
-Prior to a stable v1.0.0 release, the opkg verison policy shall be:
-
-- 0.x.y releases
- - Increment y for releases including bugfixes and minor new features only.
- Tests and documentation may be expanded. Backwards comparability should be
- maintained.
- - Increment x for releases including major new features. Backwards
- compatibility may be broken but only with good reasons which should be
- clearly documented.
-
-For opkg, the public API is the opkg program (and its command line
-arguments), the configuration options, the API of libopkg and the on-disk
-structures of package files, status and control files, etc.
-
-The format of on-disk data structures (package files, status and control files,
-etc) should be preserved much more carefully and should not be changed without
-very strong reasons. If such a change is made, an upgrade path needs to be
-offered to users.
diff --git a/docs/MAINTAINING.md b/docs/MAINTAINING.md
new file mode 100644
index 0000000..696e69e
--- /dev/null
+++ b/docs/MAINTAINING.md
@@ -0,0 +1,21 @@
+# Maintaining Opkg
+
+This document contains policy documentation and helpful references for how this fork of the Opkg project is maintained and released.
+
+
+## Version Policy
+
+This fork follows the semantic versioning specification outlined [here](https://semver.org/spec/v2.0.0.html) as SemVer 2.0.
+
+In short...
+```
+Given a version number MAJOR.MINOR.PATCH, increment the:
+
+ 1. MAJOR version when you make incompatible API changes
+ 2. MINOR version when you add functionality in a backwards compatible manner
+ 3. PATCH version when you make backwards compatible bug fixes
+
+Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
+```
+
+**Public API.** For Opkg, the public API is the `opkg` CLI application; the opkg configuration files; the public API of the libopkg library; and the on-disk structure of OPK/IPK package files, status, and control files.

Alex Stewart

unread,
Jan 17, 2023, 11:38:35 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The Release document provides an outdated view of what the release
workflow looked like for opkg at some time.

Move this information into the MAINTAINING.md document, and update it
with better information.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
developer-doc/Release | 42 ----------------------
docs/MAINTAINING.md | 81 +++++++++++++++++++++++++++++++++++++++++--
2 files changed, 79 insertions(+), 44 deletions(-)
delete mode 100644 developer-doc/Release

diff --git a/developer-doc/Release b/developer-doc/Release
deleted file mode 100644
index c04303f..0000000
--- a/developer-doc/Release
+++ /dev/null
@@ -1,42 +0,0 @@
-Releasing opkg
-==============
-
-## Release location
-
-http://downloads.yoctoproject.org/releases/opkg/
-
-## Release proceedure
-
-1) Update NEWS, adding an entry to the top of the file detailling changes from
- the last release. Commit this change.
-
-2) Update the version number in configure.ac. Commit this change.
-
-3) Tag the previous commit with the new version number, prefixed with 'v', eg.
- 'v0.3.0'. Use 'git tag -s' to produce a signed tag so that the release can
- be trusted.
-
-4) Run './autogen.sh && ./configure' to prepare the source tree.
-
-5) Run 'make dist'. This will generate 'opkg-$VERSION.tar.gz'.
-
-6) Sign the generated file using 'gpg -sab opkg-$VERSION.tar.gz'.
-
-7) Write release notes (probably copy pasta from NEWS) into the file
- 'opkg-$VERSION.release-notes'.
-
-8) Add these files to the release directory. If a local copy of the release
- directory doesn't exist, clone it from the public URL listed above using
- wget.
-
-9) Update sums files by running 'md5sum opkg-* > MD5SUMS',
- 'sha1sum opkg-* > SHA1SUMS' and 'sha256sum opkg-* > SHA256SUMS' in the
- release directory.
-
-10) Upload the new files to the release location on the Yocto Project download
- server.
-
-11) Update the opkg project page at 'http://code.google.com/p/opkg/' to link to
- the new release.
-
-12) Announce the release by email to 'opkg-...@googlegroups.com'.
diff --git a/docs/MAINTAINING.md b/docs/MAINTAINING.md
index 696e69e..959ba8a 100644
--- a/docs/MAINTAINING.md
+++ b/docs/MAINTAINING.md
@@ -3,7 +3,10 @@
This document contains policy documentation and helpful references for how this fork of the Opkg project is maintained and released.


-## Version Policy
+----
+## Policies
+
+### Version Policy

This fork follows the semantic versioning specification outlined [here](https://semver.org/spec/v2.0.0.html) as SemVer 2.0.

@@ -18,4 +21,78 @@ Given a version number MAJOR.MINOR.PATCH, increment the:
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.
```

-**Public API.** For Opkg, the public API is the `opkg` CLI application; the opkg configuration files; the public API of the libopkg library; and the on-disk structure of OPK/IPK package files, status, and control files.
\ No newline at end of file
+**Public API.** For Opkg, the public API is the `opkg` CLI application; the opkg configuration files; the public API of the libopkg library; and the on-disk structure of OPK/IPK package files, status, and control files.
+
+
+### Release Policy
+
+This fork's maintainer will create a stable release on a semi-annual basis (in June and December) by tagging the ultimate release commit with a release tag and uploading a canonical archive of the release source to the yocto project's [opkg download mirror](http://downloads.yoctoproject.org/releases/opkg/). For more detailed information, see the "Releasing Opkg" section below.
+
+If there have been no substantial changes to the project source since the prior release, the maintainer might choose to skip that release. This decision should ideally be announced on the mailing list.
+
+
+----
+## Releasing Opkg
+
+### Requirements
+
+To carry out the steps in this section, you must have:
+1. **permission** from the Yocto project to act as maintainer (obviously)
+ * If you believe it is appropriate for you to be a maintainer, start a discussion on the opkg-devel mailing list (see CONTRIBUTING) and on the Yocto project mailing list <yo...@lists.yoctoproject.org>.
+
+2. a consistent **GPG signing keypair**, for artifact signing.
+ * Google around for how to make one.
+
+3. a yocto project (Linux Foundation) **SSH account**, to upload the opkg release information.
+ * If you do not have an account, contact Michael Halstead <mhal...@linuxfoundation.org>.
+
+
+### Workflow
+
+1. Update the [CHANGELOG](CHANGELOG.md) with a summary of the notable changes since the prior release.
+
+2. Based on the changes, set the version for the release in the `configure.ac` file. Bump the version numbers according to the Version Policy above. Commit the release version.
+
+3. Create the release artifacts using the `:scripts/make-dist.sh` script.
+ ```bash
+ bash scripts/make-dist.sh -k ${YOUR_GPG_PRIVATE_KEY} HEAD
+ ```
+
+ The script should create a complete set of release artifacts in a `:dist/` directory.
+
+4. (Optional) Test the dist build in an OpenEmbedded-Core build.
+
+5. Tag and sign the release commit with the `${VERSION}` string.
+ ```bash
+ git tag --sign --local-user=${GPG_KEY_ID} -m "Opkg release $VERSION" v${VERSION}
+ ```
+
+6. Add the "`+git`" suffix to the version string in the `AC_INIT()` call in [`configure.ac`](configure.ac), to support next-cycle's development.
+
+7. Push the `:dist/` artifacts to the yocto download server using SSH.
+ ```bash
+ ssh ${USER}@access.yocto.io "mkdir -p ~/opkg; rm -v ~/opkg/*"
+ rsync ./dist/* ${USER}@access.yocto.io:~/opkg
+ ```
+ Notify Michael Halstead <mhal...@linuxfoundation.org> that there is a new opkg release at that path which is ready to be published to the mirror.
+
+8. Publish the release commits and tag to the [Yocto git server](https://git.yoctoproject.org/opkg/).
+ ```bash
+ git push yocto HEAD:master
+ git push yocto v${VERSION}
+ ```
+
+9. Make an announcement to the opkg-devel mailing list that the new release is available. Include the release notes in the email.
+
+10. Prepare an upgrade commit for the [OE-core opkg recipe](https://git.openembedded.org/openembedded-core/tree/meta/recipes-devtools/opkg). Once the source tar is available on the download mirror, upstream the recipe upgrade with the OE-core mailing list.
+
+ ```
+ opkg: upgrade to version ${VERSION}
+
+ Opkg ${VERSION} changes:
+ <snip from the release log>
+
+ ${SIGNOFF}
+ ```
+
+11. Add the released version to the enum of `versions` in the [opkg project config](https://bugzilla.yoctoproject.org/editversions.cgi?product=opkg) on bugzilla.

Alex Stewart

unread,
Jan 17, 2023, 11:38:42 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The GettingStarted document contains a mix of sparse references to the
upstream bug tracker/mailing list - which is now provided in the
CONTRIBUTING.md - and some musing about future project initiatives,
which would be better moved to another document.

Remove it.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
developer-doc/GettingStarted | 51 ------------------------------------
1 file changed, 51 deletions(-)
delete mode 100644 developer-doc/GettingStarted

diff --git a/developer-doc/GettingStarted b/developer-doc/GettingStarted
deleted file mode 100644
index 6f88fcf..0000000
--- a/developer-doc/GettingStarted
+++ /dev/null
@@ -1,51 +0,0 @@
-Getting started with opkg development
-=====================================
-
-This is a brief list of areas where new contributors can help with opkg.
-
-## General tasks
-
-- Test cases
-
- - opkg has a set of fairly self-explanatory test cases in 'tests/regress'.
- Each test is a python script which is executed from the Makefile in that
- directory. Adding test cases should be fairly straightforward if you have
- experience using opkg on the command line, little or no knowledge of the
- internals of opkg should be needed.
-
-- Refactor long/complex functions
-
- - There are still several 100+ line functions within the opkg code base that
- should be refactored into a sequence of shorter functions which each do
- just one thing.
-
-## Specific skills needed
-
-If you have any knowledge in these areas, we could really use help!
-
-- Build system
-
- - opkg currently uses autotools but we don't have anyone regularly involved
- in the project who knows this system well. If you're good with autotools,
- the build scripts need some review and probably aren't perfect. If you
- know another system well, especially cmake, we may be interested in
- changing the build system to this instead of autotools.
-
-- Man pages
-
- - Again, no-one involved in opkg at the moment is an expert on man page
- syntax. If you are, the documentation could use a lot of work!
-
-## Other things to check
-
-- Issue tracker [1]
-
- - Some of the issues may be suitable for new contributors, have a look and
- use your judgement.
-
-- Mailing list [2]
-
- - Just ask :)
-
-[1]: https://bugzilla.yoctoproject.org/buglist.cgi?product=opkg
-[2]: opkg-...@googlegroups.com
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:49 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The existing README doesn't provide very useful information to users who
find the project. They learn that opkg is a "package management system"
(whatever that means) derived from Ipkg (which they have never heard
of), and little else of value.

Instead, make the README a markdown formatted document answering basic
questions like: What is opkg? How do I get it? How do I use it? and
Where should I go for more information?

Some of the answers to those questions aren't satisfying, but those are
the subject of future initiatives.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
README | 13 -------------
README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 50 insertions(+), 13 deletions(-)
delete mode 100644 README
create mode 100644 README.md

diff --git a/README b/README
deleted file mode 100644
index ae792f8..0000000
--- a/README
+++ /dev/null
@@ -1,13 +0,0 @@
-OPKG Package Management System
-==============================
-
-Opkg is a lightweight package management system based on Ipkg.
-
-Website: http://code.google.com/p/opkg/
-Mailing list: http://groups.google.com/group/opkg-devel
-Bug tracker: https://bugzilla.yoctoproject.org/buglist.cgi?product=opkg
-Git repository: git://git.yoctoproject.org/opkg.git
-
-The previous website can still be found at:
-http://wiki.openmoko.org/wiki/Opkg
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4da3a66
--- /dev/null
+++ b/README.md
@@ -0,0 +1,50 @@
+# OPKG Package Manager
+
+Opkg is a lightweight distribution package manager, optimized for embedded applications and static image generation.
+
+This repo is part of [The Yocto Project](https://www.yoctoproject.org/), which uses Opkg as the default package manager in [OpenEmbedded](https://www.openembedded.org/wiki/Main_Page).
+
+This project is a sibling to the [OpenWRT/opkg](https://openwrt.org/docs/guide-user/additional-software/opkg) project, and both are descendants of the discontinued [Itsy Package Manager (ipkg)](https://en.wikipedia.org/wiki/Ipkg).
+
+
+* Website: http://code.google.com/p/opkg/
+* Mailing List:http://groups.google.com/group/opkg-devel
+* Bug Tracker: https://bugzilla.yoctoproject.org/buglist.cgi?product=opkg
+* Git Repository: https://git.yoctoproject.org/opkg/
+
+
+----
+## Getting Opkg
+
+If you are using a distribution built by OpenEmbedded, your distribution vendor may have installed Opkg to your system. Try calling `opkg --help` from your command line.
+
+Otherwise, you'll need to compile your own version of opkg from the sources in this repo. Follow the steps in the [`CONTRIBUTING.md`](/docs/CONTRIBUTING.md) "Building Opkg" section to generate the binaries.
+
+
+----
+## Using Opkg
+
+Opkg is intentionally designed to resemble (and be largely compatible with) the Debian project's [dpkg](https://en.wikipedia.org/wiki/Dpkg) manager. Once you have `opkg` installed to your system and a valid opkg configuration, you can manage packages on your system with commands like...
+
+```bash
+opkg install /my-package.ipk # to install a package from disk
+opkg remove my-package # to remove a package
+
+opkg update # to fetch the latest index of a remote package feed
+opkg install remote-package # to install a package from a remote feed
+opkg upgrade remote-package # to upgrade an installed package
+# et cetera
+
+opkg --help # for more information
+```
+
+
+----
+## Contributing to Opkg
+
+See the [`CONTRIBUTING`](/docs/CONTRIBUTING.md) guide and other developer documentation in the `:docs/` directory, for information on how to build, test, and upstream your changes to this project. Thanks for your help!
+
+----
+## License
+
+This project is licensed under GPL v2. See [`COPYING`](/COPYING) for the license text.

Alex Stewart

unread,
Jan 17, 2023, 11:38:55 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
GNU Automake asserts that projects should follow GNU "conventions" and
include some standard files named AUTHORS, INSTALL, ChangeLog, et c. The
intention here is probably to encourage users to distribute this
information, but it is rather heavy-handed to require that they be
specifically named in that fashion.

As a result, automake will error if we try to rename or move these
files.

Declare `-Werror foreign` in the autoconf file, which declares that we
are not a strictly GNU-compliant project in this regard, and squashes
automake's objections.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 664fd2d..318ad47 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@ AC_CONFIG_SRCDIR([libopkg/pkg.c])
AC_CONFIG_AUX_DIR([conf])
AC_CONFIG_MACRO_DIR([m4])

-AM_INIT_AUTOMAKE([subdir-objects])
+AM_INIT_AUTOMAKE([ -Werror foreign subdir-objects])
AC_CONFIG_HEADERS(config.h)

AC_CANONICAL_HOST
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:38:59 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The recent changes to the project's developer documentation have broken
compliance with how we have setup our dist content.

Update the dist content to reflect the project's current file structure.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
Makefile.am | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 1f6a784..6ac54b8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,8 +11,13 @@ intercept_DATA = intercept/ldconfig intercept/depmod intercept/update-modules
install-data-hook:
chmod +x $(DESTDIR)$(datadir)/opkg/intercept/*

-EXTRA_DIST = $(intercept_DATA) CONTRIBUTING ChangeLog.ipkg \
- developer-doc tests scripts
+EXTRA_DIST = \
+ $(intercept_DATA) \
+ docs/ \
+ scripts/ \
+ tests/ \
+ README.md \
+ CHANGELOG*

run-tests:
$(MAKE) -C tests DATADIR=@datadir@ SYSCONFDIR=@sysconfdir@ VARDIR=@localstatedir@
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:39:05 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
Add a README.md file to the tests/ subdirectory, with helpful
information to developers about how to run and write the integrated test
framework.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
docs/CONTRIBUTING.md | 4 +-
tests/README.md | 91 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+), 3 deletions(-)
create mode 100644 tests/README.md

diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 9b9eeb6..a0e8fec 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -90,9 +90,7 @@ opkg --version

Once you have *built* your change, you can validate your changes using the integrated opkg test suite, in the `:tests/` directory.

-The simplest way to run the tests is using the check make-target (`make check`). This target will compile your changes (if necessary), and then run the whole suite of functional and regression tests against the compiled opkg binary.
-
-Some regression tests are expected to fail when using either the internal satisfaction solver or libsolv; they will display as such when run. Please ensure that all tests are passing before submitting your changes to the mailing list.
+See the [`:tests/README.md`](/tests/README.md) document for information about how to run the test suite and author new tests. (tldr; run `make check`)


----
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..cb1488f
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,91 @@
+# Opkg Tests
+
+This directory contains the Opkg integrated test suite.
+
+This test framework detects errors in the functional capabilities of an opkg binary (the `core/` tests), and checks for known bugs (the `regress/` tests).
+
+This framework is written in python and uses shell calls to interact with an `opkg` binary under-test. You can test a compiled `opkg` binary directly from the source tree (without installing it to your system root). The temporary data created by this framework are stored in your system `TMPDIR` and nothing should be installed to your system root.
+
+
+----
+## Running the Tests
+
+The simplest way to run this test suite is to `cd` to the top-level directory and run `make check`. The `check` make-target will compile your changes (if necessary), and then run the whole suite of functional and regression tests against the compiled `opkg` binary.
+
+Some regression tests are expected to fail when using either the internal satisfaction solver or libsolv; they will display as such when run. Please ensure that all tests are passing before submitting your changes to the mailing list.
+
+
+### Test Configuration
+
+The test framework uses configuration settings stored in the [`:tests/cfg.py`](/tests/cfg.py) file. By editing the values in that file, you can change the `TMPDIR` location that is used to host the framework's virtual workspace.
+
+You can further tweak the subpaths that opkg uses for its data and configuration directories *within the test workspace*, by setting the `DATADIR`, `SYSCONFDIR`, and `VARDIR` environment variables during test execution. See the top-level `Makefile` (after automaking it) for a hint as to how those variable are normally defined.
+
+
+### Running Single Tests
+
+When debugging an issue or doing new development, it is sometimes helpful to run a single test case (or a subset of them) against your changes. The easiest way to do this is to overwrite the `REGRESSION_TESTS` variable like
+
+```Make
+REGRESSION_TESTS := test/you/care/about.py
+```
+
+Then just run `make check` again.
+
+
+### Opkg Debug Output
+
+The `opkgcl` module tests for an environment variable `OPKG_DEBUG_CMDS`. If it is set to "True", the `stderr` and `stdout` from opkg calls made during the test run will be printed to `stdout`.
+
+
+----
+## Authoring New Tests
+
+If you need to author a new test, it is probably to reproduce and validate a discovered issue. Most test cases follow the workflow below, and it is a good idea to use the existing tests as a reference for how to accomplish common steps.
+
+1. Create a new test case file under the `regress/` directory, named with the bugzilla issue number associated with the issue.
+
+2. Start your test file with the common opkg issue template below. Filling this out will help others to understand what your test case is trying to reproduce, and also serve as a guide for you when implementing the test.
+
+```
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Reporter: $the_bug_reporter_contact_info
+#
+# What steps will reproduce the problem?
+########################################
+#
+# #TODO
+#
+# What is the expected output? What do you see instead?
+########################################
+# Expected:
+# #TODO
+# Actual:
+# #TODO
+```
+
+3. Import the `opk` and `opkgcl` python libraries, which provide utility functions for creating dummy packages and feeds, and instrumenting `opkg` binary calls respectively. Optionally, you may also wish to import the `cfg` module, if you need to access the paths defined within it during your test.
+
+ ```python
+ import cfg, opk, opkgcl
+ ```
+
+4. Run the `opk.regress_init()` method to tell the framework that you are starting a new test case.
+
+5. Use the `opk` module to create a new `OpkGroup` (a package feed), and populate it with Packages that will reproduce your issue.
+
+6. Call `opkgcl.update()` to load your dummy package group's index into the test framework's opkg environment. Then use the module to exercise the `opkg` calls which will reproduce your issue.
+
+7. Use the `opk.fail()` method to fail the test when you have detected that the bug reproduces.
+
+
+----
+## Layout
+
+* `cfg.py` : Defines test framework configuration variables.
+* `opk.py` : Defines a helper module for constructing dummy packages and package feeds, for use by the testing framework.
+* `opkgcl.py` : Defines a wrapper module for interacting with a compiled opkg binary; either installed to the system PATH or from the source tree.
+* `core/` : Test cases which exercise core opkg functionality.
+* `regress/` : Test cases for exercising abnormal behavior identified in bugs.

Alex Stewart

unread,
Jan 17, 2023, 11:39:11 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The configure.lineno file is autogenerated by ./configure and should not
be source-controlled.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
.gitignore | 1 +
1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index 44199f7..1625dca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ INSTALL
# Auto-generated config header and timestamp
config.h
config.h.in
+configure.lineno
stamp-h1

# The configure script itself
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:39:16 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The TODO file contains some historic musings about things to improve in
the codebase. For most entries, the discussion history has been lost and
the entries themselves aren't enough to take discrete action.

The exception is the request to improve the efficiency of the
pkg_hash_best_installation_candidate function, which has been moved to
this bugzilla.

https://bugzilla.yoctoproject.org/show_bug.cgi?id=15006

Remove the file.

There is some space in the project to keep a roadmap document with
future initiatives that are more than individual enhancements or bugs,
and that document is in-work.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
TODO | 49 -------------------------------------------------
1 file changed, 49 deletions(-)
delete mode 100644 TODO

diff --git a/TODO b/TODO
deleted file mode 100644
index 47a6cda..0000000
--- a/TODO
+++ /dev/null
@@ -1,49 +0,0 @@
-BUGS
-====
-
-See issue list: http://code.google.com/p/opkg/issues/list
-
- * Resolve 'XXX', 'FIXME' and 'TODO' comments in the code.
-
- * Remove dead and duplicate code. Refactor duplicated functionality.
-
- * Remove pkg_info_preinstall_check().
-
- * Reduce memory used per pkg_t and peak memory use in general.
-
- * Refactor opkg_install_pkg() into more precise functions.
-
- * pkg_hash_fetch_best_installation_candidate() is linear search O(P*PN)
- and is slow (frequently called).
- P provider
- PN pkgs in a provider
- It can be O(P) if a hash table is used.
-
-Solver bugs:
-
- * opkg_list_upgradable_cmd() does not work with an external solver enabled.
-
- * Fix hacky way libsolv's arch comparisons are bypassed when
- prefer_arch_to_version is not set in libsolv_solver_set_arch_policy().
- See openSUSE/libsolv issue #98 on github.
-
- * libsolv's upgrade will not automatically remove orphaned packages.
- This causes failures of tests core/18_upgrade_recommends.py
- and misc/update_loses_autoinstalled_flag.py.
-
- * The different order in which libsolv visits packages can cause
- some file ownership problems causing regress/issue50.py to fail
- because of the same problem as issue 156.
-
- * libsolv supports dist-upgrade. The functionality to use it is implemented
- in opkg_solver.c and opkg_solver_libsolv.c. Add an option in opkg_cmd to
- use this.
-
- * for libsolv get correct package during a reinstall when there are two
- packages with the same name and version, and revert hack in pkg_hash.c
-
-FEATURES
-========
-
- * Improve dpkg compatibility, according to the Debian Policy Manual.
- http://www.debian.org/doc/debian-policy/ch-controlfields.html
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:39:21 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
Add a document which espouses the larger-scale initiatives which would
improve the project. This list is a combination of the tribal knowledge
that I have learned as the project maintainer, and the historic TODOs
and commentary of past maintainers.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
docs/ROADMAP.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 51 insertions(+)
create mode 100644 docs/ROADMAP.md

diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
new file mode 100644
index 0000000..4c462f1
--- /dev/null
+++ b/docs/ROADMAP.md
@@ -0,0 +1,51 @@
+# Opkg Roadmap
+
+This document contains historic context for this project which developers and maintainers might be interested to know, as well as a prospectus for future development. This document is the domain of the current maintainer.
+
+If you are interested in advancing any of the initiatives in this document, it would be a major help to the community and earn you a lot of good will. :)
+
+
+----
+## Future Development Initiatives
+
+Entries in this section marked with **[1.0]** must be completed before we call Opkg version 1 "stable".
+
+
+### General
+
+* Convert the project to use build tooling which is more modern than autotools. It seems the only reason autotools was chosen for the initial implementation was that "it worked". But no one was an expert at it and the existing implementation is rather old and crusty. It pre-dates git and tends to leave clutter around, and doesn't understand modern project layouts. Investigate the modern ecology of C/C++ build tooling (start w/ cmake) and try to follow the winds to find something better.
+
+* **[1.0]** Move the project off of Google Code; the service has been officially deprecated for many years and might just disappear one day. It isn't a stable or modern host for our mailing list. You can't even subscribe to the mailing list without either having a gmail account, or using an undocumented email API.
+
+* Publish a docker container which can build opkg. This is a nice-to-have for any modern project, since it can get developers onboarded with making and testing contributions quickly. Start by talking to Alex Stewart, who already has a container that he uses for his development.
+
+* Publish a docker container which runs opkg. It is fairly common contemporarily to give users a one-button way to run your application in a sandbox and play around with its abilities. I think this would go a long way towards driving opkg adoption.
+
+
+### libopkg
+
+* Remove the 'XXX', 'FIXME', and 'TODO' comments in the code. If they're real objectives that should be fixed, we should create bugs for them, not talk about them in code where no one will see them.
+
+* **[1.0]** Deprecate the internal sat solver implementation. `libsolv` has been the preferred backend for many years, to the extent that internal solver bugs aren't even prioritized on the bug backlog. Ask the community if there is *anyone* who is still using the internal solver, and move to remove it entirely and make libsolv the default.
+
+
+### Testing
+
+* Reimplement the test framework in a more modern test tool like [pytest](https://docs.pytest.org/en/7.2.x/).
+
+* Develop an efficient way to test large (virtual) package feeds. Some issues (eg. https://bugzilla.yoctoproject.org/show_bug.cgi?id=13593) report poor opkg performance when using reasonably large package feeds. These issues are difficult to test and develop-for, because it is expensive in the current framework to generate thousands of packages on disk for a test case. However, there is no real reason that these files have to exist all the time. If we leveraged something like a [flask server](https://flask.palletsprojects.com/en/2.2.x/), we could generate a package index and thousands of "virtual packages" on-demand, and only deliver opkg the content that it requests.
+
+* **[1.0]** Revisit the `:tests/core/` test cases. They have not seen new content in many years, and I (astewart) do not believe that they represent a decent set of coverage for the core functionality of opkg.
+
+* Make the test framework work with both `opkg` and `dpkg` simultaneously. Many discussion threads in the opkg community start with asking "What does dpkg do in this case?". We should have easy tooling for testing arbitrary package setups in dpkg, and comparing opkg's functionality against it.
+
+* **[1.0]** Stand up an auto-test suite. Opkg already has a reasonably reliable integrated test suite. There is no reason that the project shouldn't have an autotester somewhere which can test incoming patchsets. In fact, Alex Stewart already has a personal gitlab instance (https://gitlab.com/astewart.49c6/opkg) with an autotest pipeline that uses the free resources that GitLab provides.
+
+
+### Documentation
+
+* **[1.0]** Update the opkg `man` pages.
+
+* **[1.0]** Document the Opkg API; it is a requirement for taking Opkg to an official version 1.0 release.
+
+* **[1.0]** Publish the opkg man pages and API documentation to the internet somewhere. New users who are looking for help using opkg will likely find the [OpenWRT docs](https://openwrt.org/docs/guide-user/additional-software/opkg), which do not reflect how this fork operates.
--
2.38.1

Alex Stewart

unread,
Jan 17, 2023, 11:39:29 AM1/17/23
to opkg-...@googlegroups.com, Alex Stewart
The checkpatch.pl and test-branch.py files are obsolescent and no longer
recommended for new developers to run on their patches.

Remove the scripts, so that devs don't run them by mistake.

Signed-off-by: Alex Stewart <alex.s...@ni.com>
---
scripts/checkpatch.pl | 3730 ----------------------------------------
scripts/test-branch.py | 84 -
2 files changed, 3814 deletions(-)
delete mode 100755 scripts/checkpatch.pl
delete mode 100755 scripts/test-branch.py

diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
deleted file mode 100755
index 15e2411..0000000
--- a/scripts/checkpatch.pl
+++ /dev/null
@@ -1,3730 +0,0 @@
-#!/usr/bin/perl -w
-#
-# 'scripts/checkpatch.sh', taken from the Linux kernel v3.15 and merged into
-# opkg. All the Linux-kernel specific checks which are irrelevant to opkg have
-# subsequently been removed.
-#
-# From time-to-time this script should probably be synced against any upstream
-# changes to checkpatch.pl in the kernel.
-#
-# (c) 2001, Dave Jones. (the file handling bit)
-# (c) 2005, Joel Schopp <jsc...@austin.ibm.com> (the ugly bit)
-# (c) 2007,2008, Andy Whitcroft <a...@uk.ibm.com> (new conditions, test suite)
-# (c) 2008-2010 Andy Whitcroft <a...@canonical.com>
-# SPDX-License-Identifier: GPL-2.0-only
-# Licensed under the terms of the GNU GPL License version 2
-
-use strict;
-use POSIX;
-
-my $P = $0;
-$P =~ s@.*/@@g;
-
-my $V = '0.32';
-
-use Getopt::Long qw(:config no_auto_abbrev);
-
-my $quiet = 0;
-my $chk_signoff = 1;
-my $chk_patch = 1;
-my $tst_only;
-my $emacs = 0;
-my $terse = 0;
-my $file = 0;
-my $check = 1;
-my $check_orig = 0;
-my $summary = 1;
-my $mailback = 0;
-my $summary_file = 0;
-my $show_types = 0;
-my $fix = 0;
-my $fix_inplace = 0;
-my %debug;
-my %use_type = ();
-my @use = ();
-my %ignore_type = ();
-my @ignore = ();
-my $help = 0;
-my $configuration_file = ".checkpatch.conf";
-my $max_line_length = 80;
-my $ignore_perl_version = 0;
-my $minimum_perl_version = 5.10.0;
-my $hardtabs = 0;
-
-sub help {
- my ($exitcode) = @_;
-
- print << "EOM";
-Usage: $P [OPTION]... [FILE]...
-Version: $V
-
-Options:
- -q, --quiet quiet
- --no-signoff do not check for 'Signed-off-by' line
- --patch treat FILE as patchfile (default)
- --emacs emacs compile window format
- --terse one line per report
- -f, --file treat FILE as regular source file
- --subjective, --strict enable more subjective tests
- --hard-tabs force hard tabs instead of soft tabs
- --types TYPE(,TYPE2...) show only these comma separated message types
- --ignore TYPE(,TYPE2...) ignore various comma separated message types
- --max-line-length=n set the maximum line length, if exceeded, warn
- --show-types show the message "types" in the output
- --no-summary suppress the per-file summary
- --mailback only produce a report in case of warnings/errors
- --summary-file include the filename in summary
- --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of
- 'values', 'possible', 'type', and 'attr' (default
- is all off)
- --test-only=WORD report only warnings/errors containing WORD
- literally
- --fix EXPERIMENTAL - may create horrible results
- If correctable single-line errors exist, create
- "<inputfile>.EXPERIMENTAL-checkpatch-fixes"
- with potential errors corrected to the preferred
- checkpatch style
- --fix-inplace EXPERIMENTAL - may create horrible results
- Is the same as --fix, but overwrites the input
- file. It's your fault if there's no backup or git
- --ignore-perl-version override checking of perl version. expect
- runtime errors.
- -h, --help, --version display this help and exit
-
-When FILE is - read standard input.
-EOM
-
- exit($exitcode);
-}
-
-my $conf = which_conf($configuration_file);
-if (-f $conf) {
- my @conf_args;
- open(my $conffile, '<', "$conf")
- or warn "$P: Can't find a readable $configuration_file file $!\n";
-
- while (<$conffile>) {
- my $line = $_;
-
- $line =~ s/\s*\n?$//g;
- $line =~ s/^\s*//g;
- $line =~ s/\s+/ /g;
-
- next if ($line =~ m/^\s*#/);
- next if ($line =~ m/^\s*$/);
-
- my @words = split(" ", $line);
- foreach my $word (@words) {
- last if ($word =~ m/^#/);
- push (@conf_args, $word);
- }
- }
- close($conffile);
- unshift(@ARGV, @conf_args) if @conf_args;
-}
-
-GetOptions(
- 'q|quiet+' => \$quiet,
- 'signoff!' => \$chk_signoff,
- 'patch!' => \$chk_patch,
- 'emacs!' => \$emacs,
- 'terse!' => \$terse,
- 'f|file!' => \$file,
- 'subjective!' => \$check,
- 'hard-tabs!' => \$hardtabs,
- 'strict!' => \$check,
- 'ignore=s' => \@ignore,
- 'types=s' => \@use,
- 'show-types!' => \$show_types,
- 'max-line-length=i' => \$max_line_length,
- 'summary!' => \$summary,
- 'mailback!' => \$mailback,
- 'summary-file!' => \$summary_file,
- 'fix!' => \$fix,
- 'fix-inplace!' => \$fix_inplace,
- 'ignore-perl-version!' => \$ignore_perl_version,
- 'debug=s' => \%debug,
- 'test-only=s' => \$tst_only,
- 'h|help' => \$help,
- 'version' => \$help
-) or help(1);
-
-help(0) if ($help);
-
-$fix = 1 if ($fix_inplace);
-$check_orig = $check;
-
-my $exit = 0;
-
-if ($^V && $^V lt $minimum_perl_version) {
- printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
- if (!$ignore_perl_version) {
- exit(1);
- }
-}
-
-if ($#ARGV < 0) {
- print "$P: no input files\n";
- exit(1);
-}
-
-sub hash_save_array_words {
- my ($hashRef, $arrayRef) = @_;
-
- my @array = split(/,/, join(',', @$arrayRef));
- foreach my $word (@array) {
- $word =~ s/\s*\n?$//g;
- $word =~ s/^\s*//g;
- $word =~ s/\s+/ /g;
- $word =~ tr/[a-z]/[A-Z]/;
-
- next if ($word =~ m/^\s*#/);
- next if ($word =~ m/^\s*$/);
-
- $hashRef->{$word}++;
- }
-}
-
-sub hash_show_words {
- my ($hashRef, $prefix) = @_;
-
- if ($quiet == 0 && keys %$hashRef) {
- print "NOTE: $prefix message types:";
- foreach my $word (sort keys %$hashRef) {
- print " $word";
- }
- print "\n\n";
- }
-}
-
-hash_save_array_words(\%ignore_type, \@ignore);
-hash_save_array_words(\%use_type, \@use);
-
-my $dbg_values = 0;
-my $dbg_possible = 0;
-my $dbg_type = 0;
-my $dbg_attr = 0;
-for my $key (keys %debug) {
- ## no critic
- eval "\${dbg_$key} = '$debug{$key}';";
- die "$@" if ($@);
-}
-
-my $rpt_cleaners = 0;
-
-if ($terse) {
- $emacs = 1;
- $quiet++;
-}
-
-my $emitted_corrupt = 0;
-
-our $Ident = qr{
- [A-Za-z_][A-Za-z\d_]*
- (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
- }x;
-our $Storage = qr{extern|static|asmlinkage};
-our $Sparse = qr{
- __user|
- __kernel|
- __force|
- __iomem|
- __must_check|
- __init_refok|
- __kprobes|
- __ref|
- __rcu
- }x;
-# Notes to $Attribute:
-# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
-our $Attribute = qr{
- const
- }x;
-our $Modifier;
-our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__};
-our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]};
-our $Lval = qr{$Ident(?:$Member)*};
-
-our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u};
-our $Binary = qr{(?i)0b[01]+$Int_type?};
-our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?};
-our $Int = qr{[0-9]+$Int_type?};
-our $Octal = qr{0[0-7]+$Int_type?};
-our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
-our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
-our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?};
-our $Float = qr{$Float_hex|$Float_dec|$Float_int};
-our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int};
-our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
-our $Compare = qr{<=|>=|==|!=|<|(?<!-)>};
-our $Arithmetic = qr{\+|-|\*|\/|%};
-our $Operators = qr{
- <=|>=|==|!=|
- =>|->|<<|>>|<|>|!|~|
- &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
- }x;
-
-our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
-
-our $NonptrType;
-our $Type;
-our $Declare;
-
-our $NON_ASCII_UTF8 = qr{
- [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
- | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
- | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
- | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
- | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
- | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
- | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
-}x;
-
-our $UTF8 = qr{
- [\x09\x0A\x0D\x20-\x7E] # ASCII
- | $NON_ASCII_UTF8
-}x;
-
-our $typeTypedefs = qr{(?x:
- (?:__)?(?:u|s|be|le)(?:8|16|32|64)|
- atomic_t
-)};
-
-our $logFunctions = qr{(?x:
- opkg_msg
-)};
-
-our $signature_tags = qr{(?xi:
- Signed-off-by:|
- Acked-by:|
- Tested-by:|
- Reviewed-by:|
- Reported-by:|
- Suggested-by:|
- To:|
- Cc:
-)};
-
-our @typeList = (
- qr{void},
- qr{(?:unsigned\s+)?char},
- qr{(?:unsigned\s+)?short},
- qr{(?:unsigned\s+)?int},
- qr{(?:unsigned\s+)?long},
- qr{(?:unsigned\s+)?long\s+int},
- qr{(?:unsigned\s+)?long\s+long},
- qr{(?:unsigned\s+)?long\s+long\s+int},
- qr{unsigned},
- qr{float},
- qr{double},
- qr{bool},
- qr{struct\s+$Ident},
- qr{union\s+$Ident},
- qr{enum\s+$Ident},
- qr{${Ident}_t},
- qr{${Ident}_handler},
- qr{${Ident}_handler_fn},
-);
-
-our @modifierList = (
- qr{fastcall},
-);
-
-sub build_types {
- my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)";
- my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)";
- $Modifier = qr{(?:$Attribute|$Sparse|$mods)};
- $NonptrType = qr{
- (?:$Modifier\s+|const\s+)*
- (?:
- (?:typeof|__typeof__)\s*\([^\)]*\)|
- (?:$typeTypedefs\b)|
- (?:${all}\b)
- )
- (?:\s+$Modifier|\s+const)*
- }x;
- $Type = qr{
- $NonptrType
- (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*|\[\])+|(?:\s*\[\s*\])+)?
- (?:\s+$Inline|\s+$Modifier)*
- }x;
- $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
-}
-build_types();
-
-our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
-
-# Using $balanced_parens, $LvalOrFunc, or $FuncArg
-# requires at least perl version v5.10.0
-# Any use must be runtime checked with $^V
-
-our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
-our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
-our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant)};
-
-sub deparenthesize {
- my ($string) = @_;
- return "" if (!defined($string));
-
- while ($string =~ /^\s*\(.*\)\s*$/) {
- $string =~ s@^\s*\(\s*@@;
- $string =~ s@\s*\)\s*$@@;
- }
-
- $string =~ s@\s+@ @g;
-
- return $string;
-}
-
-$chk_signoff = 0 if ($file);
-
-my @rawlines = ();
-my @lines = ();
-my @fixed = ();
-my $vname;
-for my $filename (@ARGV) {
- my $FILE;
- if ($file) {
- open($FILE, '-|', "diff -u /dev/null $filename") ||
- die "$P: $filename: diff failed - $!\n";
- } elsif ($filename eq '-') {
- open($FILE, '<&STDIN');
- } else {
- open($FILE, '<', "$filename") ||
- die "$P: $filename: open failed - $!\n";
- }
- if ($filename eq '-') {
- $vname = 'Your patch';
- } else {
- $vname = $filename;
- }
- while (<$FILE>) {
- chomp;
- push(@rawlines, $_);
- }
- close($FILE);
- if (!process($filename)) {
- $exit = 1;
- }
- @rawlines = ();
- @lines = ();
- @fixed = ();
-}
-
-exit($exit);
-
-sub parse_email {
- my ($formatted_email) = @_;
-
- my $name = "";
- my $address = "";
- my $comment = "";
-
- if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) {
- $name = $1;
- $address = $2;
- $comment = $3 if defined $3;
- } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) {
- $address = $1;
- $comment = $2 if defined $2;
- } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
- $address = $1;
- $comment = $2 if defined $2;
- $formatted_email =~ s/$address.*$//;
- $name = $formatted_email;
- $name = trim($name);
- $name =~ s/^\"|\"$//g;
- # If there's a name left after stripping spaces and
- # leading quotes, and the address doesn't have both
- # leading and trailing angle brackets, the address
- # is invalid. ie:
- # "joe smith j...@smith.com" bad
- # "joe smith <j...@smith.com" bad
- if ($name ne "" && $address !~ /^<[^>]+>$/) {
- $name = "";
- $address = "";
- $comment = "";
- }
- }
-
- $name = trim($name);
- $name =~ s/^\"|\"$//g;
- $address = trim($address);
- $address =~ s/^\<|\>$//g;
-
- if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
- $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
- $name = "\"$name\"";
- }
-
- return ($name, $address, $comment);
-}
-
-sub format_email {
- my ($name, $address) = @_;
-
- my $formatted_email;
-
- $name = trim($name);
- $name =~ s/^\"|\"$//g;
- $address = trim($address);
-
- if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
- $name =~ s/(?<!\\)"/\\"/g; ##escape quotes
- $name = "\"$name\"";
- }
-
- if ("$name" eq "") {
- $formatted_email = "$address";
- } else {
- $formatted_email = "$name <$address>";
- }
-
- return $formatted_email;
-}
-
-sub which_conf {
- my ($conf) = @_;
-
- foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
- if (-e "$path/$conf") {
- return "$path/$conf";
- }
- }
-
- return "";
-}
-
-sub expand_tabs {
- my ($str) = @_;
-
- my $res = '';
- my $n = 0;
- for my $c (split(//, $str)) {
- if ($c eq "\t") {
- $res .= ' ';
- $n++;
- for (; ($n % 8) != 0; $n++) {
- $res .= ' ';
- }
- next;
- }
- $res .= $c;
- $n++;
- }
-
- return $res;
-}
-sub copy_spacing {
- (my $res = shift) =~ tr/\t/ /c;
- return $res;
-}
-
-sub line_stats {
- my ($line) = @_;
-
- # Drop the diff line leader and expand tabs
- $line =~ s/^.//;
- $line = expand_tabs($line);
-
- # Pick the indent from the front of the line.
- my ($white) = ($line =~ /^(\s*)/);
-
- return (length($line), length($white));
-}
-
-my $sanitise_quote = '';
-
-sub sanitise_line_reset {
- my ($in_comment) = @_;
-
- if ($in_comment) {
- $sanitise_quote = '*/';
- } else {
- $sanitise_quote = '';
- }
-}
-sub sanitise_line {
- my ($line) = @_;
-
- my $res = '';
- my $l = '';
-
- my $qlen = 0;
- my $off = 0;
- my $c;
-
- # Always copy over the diff marker.
- $res = substr($line, 0, 1);
-
- for ($off = 1; $off < length($line); $off++) {
- $c = substr($line, $off, 1);
-
- # Comments we are wacking completly including the begin
- # and end, all to $;.
- if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
- $sanitise_quote = '*/';
-
- substr($res, $off, 2, "$;$;");
- $off++;
- next;
- }
- if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
- $sanitise_quote = '';
- substr($res, $off, 2, "$;$;");
- $off++;
- next;
- }
- if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
- $sanitise_quote = '//';
-
- substr($res, $off, 2, $sanitise_quote);
- $off++;
- next;
- }
-
- # A \ in a string means ignore the next character.
- if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
- $c eq "\\") {
- substr($res, $off, 2, 'XX');
- $off++;
- next;
- }
- # Regular quotes.
- if ($c eq "'" || $c eq '"') {
- if ($sanitise_quote eq '') {
- $sanitise_quote = $c;
-
- substr($res, $off, 1, $c);
- next;
- } elsif ($sanitise_quote eq $c) {
- $sanitise_quote = '';
- }
- }
-
- #print "c<$c> SQ<$sanitise_quote>\n";
- if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
- substr($res, $off, 1, $;);
- } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
- substr($res, $off, 1, $;);
- } elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
- substr($res, $off, 1, 'X');
- } else {
- substr($res, $off, 1, $c);
- }
- }
-
- if ($sanitise_quote eq '//') {
- $sanitise_quote = '';
- }
-
- # The pathname on a #include may be surrounded by '<' and '>'.
- if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
- my $clean = 'X' x length($1);
- $res =~ s@\<.*\>@<$clean>@;
-
- # The whole of a #error is a string.
- } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
- my $clean = 'X' x length($1);
- $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
- }
-
- return $res;
-}
-
-sub get_quoted_string {
- my ($line, $rawline) = @_;
-
- return "" if ($line !~ m/(\"[X]+\")/g);
- return substr($rawline, $-[0], $+[0] - $-[0]);
-}
-
-sub ctx_statement_block {
- my ($linenr, $remain, $off) = @_;
- my $line = $linenr - 1;
- my $blk = '';
- my $soff = $off;
- my $coff = $off - 1;
- my $coff_set = 0;
-
- my $loff = 0;
-
- my $type = '';
- my $level = 0;
- my @stack = ();
- my $p;
- my $c;
- my $len = 0;
-
- my $remainder;
- while (1) {
- @stack = (['', 0]) if ($#stack == -1);
-
- #warn "CSB: blk<$blk> remain<$remain>\n";
- # If we are about to drop off the end, pull in more
- # context.
- if ($off >= $len) {
- for (; $remain > 0; $line++) {
- last if (!defined $lines[$line]);
- next if ($lines[$line] =~ /^-/);
- $remain--;
- $loff = $len;
- $blk .= $lines[$line] . "\n";
- $len = length($blk);
- $line++;
- last;
- }
- # Bail if there is no further context.
- #warn "CSB: blk<$blk> off<$off> len<$len>\n";
- if ($off >= $len) {
- last;
- }
- if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) {
- $level++;
- $type = '#';
- }
- }
- $p = $c;
- $c = substr($blk, $off, 1);
- $remainder = substr($blk, $off);
-
- #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
-
- # Handle nested #if/#else.
- if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
- push(@stack, [ $type, $level ]);
- } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
- ($type, $level) = @{$stack[$#stack - 1]};
- } elsif ($remainder =~ /^#\s*endif\b/) {
- ($type, $level) = @{pop(@stack)};
- }
-
- # Statement ends at the ';' or a close '}' at the
- # outermost level.
- if ($level == 0 && $c eq ';') {
- last;
- }
-
- # An else is really a conditional as long as its not else if
- if ($level == 0 && $coff_set == 0 &&
- (!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
- $remainder =~ /^(else)(?:\s|{)/ &&
- $remainder !~ /^else\s+if\b/) {
- $coff = $off + length($1) - 1;
- $coff_set = 1;
- #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
- #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
- }
-
- if (($type eq '' || $type eq '(') && $c eq '(') {
- $level++;
- $type = '(';
- }
- if ($type eq '(' && $c eq ')') {
- $level--;
- $type = ($level != 0)? '(' : '';
-
- if ($level == 0 && $coff < $soff) {
- $coff = $off;
- $coff_set = 1;
- #warn "CSB: mark coff<$coff>\n";
- }
- }
- if (($type eq '' || $type eq '{') && $c eq '{') {
- $level++;
- $type = '{';
- }
- if ($type eq '{' && $c eq '}') {
- $level--;
- $type = ($level != 0)? '{' : '';
-
- if ($level == 0) {
- if (substr($blk, $off + 1, 1) eq ';') {
- $off++;
- }
- last;
- }
- }
- # Preprocessor commands end at the newline unless escaped.
- if ($type eq '#' && $c eq "\n" && $p ne "\\") {
- $level--;
- $type = '';
- $off++;
- last;
- }
- $off++;
- }
- # We are truly at the end, so shuffle to the next line.
- if ($off == $len) {
- $loff = $len + 1;
- $line++;
- $remain--;
- }
-
- my $statement = substr($blk, $soff, $off - $soff + 1);
- my $condition = substr($blk, $soff, $coff - $soff + 1);
-
- #warn "STATEMENT<$statement>\n";
- #warn "CONDITION<$condition>\n";
-
- #print "coff<$coff> soff<$off> loff<$loff>\n";
-
- return ($statement, $condition,
- $line, $remain + 1, $off - $loff + 1, $level);
-}
-
-sub statement_lines {
- my ($stmt) = @_;
-
- # Strip the diff line prefixes and rip blank lines at start and end.
- $stmt =~ s/(^|\n)./$1/g;
- $stmt =~ s/^\s*//;
- $stmt =~ s/\s*$//;
-
- my @stmt_lines = ($stmt =~ /\n/g);
-
- return $#stmt_lines + 2;
-}
-
-sub statement_rawlines {
- my ($stmt) = @_;
-
- my @stmt_lines = ($stmt =~ /\n/g);
-
- return $#stmt_lines + 2;
-}
-
-sub statement_block_size {
- my ($stmt) = @_;
-
- $stmt =~ s/(^|\n)./$1/g;
- $stmt =~ s/^\s*{//;
- $stmt =~ s/}\s*$//;
- $stmt =~ s/^\s*//;
- $stmt =~ s/\s*$//;
-
- my @stmt_lines = ($stmt =~ /\n/g);
- my @stmt_statements = ($stmt =~ /;/g);
-
- my $stmt_lines = $#stmt_lines + 2;
- my $stmt_statements = $#stmt_statements + 1;
-
- if ($stmt_lines > $stmt_statements) {
- return $stmt_lines;
- } else {
- return $stmt_statements;
- }
-}
-
-sub ctx_statement_full {
- my ($linenr, $remain, $off) = @_;
- my ($statement, $condition, $level);
-
- my (@chunks);
-
- # Grab the first conditional/block pair.
- ($statement, $condition, $linenr, $remain, $off, $level) =
- ctx_statement_block($linenr, $remain, $off);
- #print "F: c<$condition> s<$statement> remain<$remain>\n";
- push(@chunks, [ $condition, $statement ]);
- if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) {
- return ($level, $linenr, @chunks);
- }
-
- # Pull in the following conditional/block pairs and see if they
- # could continue the statement.
- for (;;) {
- ($statement, $condition, $linenr, $remain, $off, $level) =
- ctx_statement_block($linenr, $remain, $off);
- #print "C: c<$condition> s<$statement> remain<$remain>\n";
- last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s));
- #print "C: push\n";
- push(@chunks, [ $condition, $statement ]);
- }
-
- return ($level, $linenr, @chunks);
-}
-
-sub ctx_block_get {
- my ($linenr, $remain, $outer, $open, $close, $off) = @_;
- my $line;
- my $start = $linenr - 1;
- my $blk = '';
- my @o;
- my @c;
- my @res = ();
-
- my $level = 0;
- my @stack = ($level);
- for ($line = $start; $remain > 0; $line++) {
- next if ($rawlines[$line] =~ /^-/);
- $remain--;
-
- $blk .= $rawlines[$line];
-
- # Handle nested #if/#else.
- if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
- push(@stack, $level);
- } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
- $level = $stack[$#stack - 1];
- } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) {
- $level = pop(@stack);
- }
-
- foreach my $c (split(//, $lines[$line])) {
- ##print "C<$c>L<$level><$open$close>O<$off>\n";
- if ($off > 0) {
- $off--;
- next;
- }
-
- if ($c eq $close && $level > 0) {
- $level--;
- last if ($level == 0);
- } elsif ($c eq $open) {
- $level++;
- }
- }
-
- if (!$outer || $level <= 1) {
- push(@res, $rawlines[$line]);
- }
-
- last if ($level == 0);
- }
-
- return ($level, @res);
-}
-sub ctx_block_outer {
- my ($linenr, $remain) = @_;
-
- my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0);
- return @r;
-}
-sub ctx_block {
- my ($linenr, $remain) = @_;
-
- my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0);
- return @r;
-}
-sub ctx_statement {
- my ($linenr, $remain, $off) = @_;
-
- my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off);
- return @r;
-}
-sub ctx_block_level {
- my ($linenr, $remain) = @_;
-
- return ctx_block_get($linenr, $remain, 0, '{', '}', 0);
-}
-sub ctx_statement_level {
- my ($linenr, $remain, $off) = @_;
-
- return ctx_block_get($linenr, $remain, 0, '(', ')', $off);
-}
-
-sub ctx_locate_comment {
- my ($first_line, $end_line) = @_;
-
- # Catch a comment on the end of the line itself.
- my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
- return $current_comment if (defined $current_comment);
-
- # Look through the context and try and figure out if there is a
- # comment.
- my $in_comment = 0;
- $current_comment = '';
- for (my $linenr = $first_line; $linenr < $end_line; $linenr++) {
- my $line = $rawlines[$linenr - 1];
- #warn " $line\n";
- if ($linenr == $first_line and $line =~ m@^.\s*\*@) {
- $in_comment = 1;
- }
- if ($line =~ m@/\*@) {
- $in_comment = 1;
- }
- if (!$in_comment && $current_comment ne '') {
- $current_comment = '';
- }
- $current_comment .= $line . "\n" if ($in_comment);
- if ($line =~ m@\*/@) {
- $in_comment = 0;
- }
- }
-
- chomp($current_comment);
- return($current_comment);
-}
-sub ctx_has_comment {
- my ($first_line, $end_line) = @_;
- my $cmt = ctx_locate_comment($first_line, $end_line);
-
- ##print "LINE: $rawlines[$end_line - 1 ]\n";
- ##print "CMMT: $cmt\n";
-
- return ($cmt ne '');
-}
-
-sub raw_line {
- my ($linenr, $cnt) = @_;
-
- my $offset = $linenr - 1;
- $cnt++;
-
- my $line;
- while ($cnt) {
- $line = $rawlines[$offset++];
- next if (defined($line) && $line =~ /^-/);
- $cnt--;
- }
-
- return $line;
-}
-
-sub cat_vet {
- my ($vet) = @_;
- my ($res, $coded);
-
- $res = '';
- while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) {
- $res .= $1;
- if ($2 ne '') {
- $coded = sprintf("^%c", unpack('C', $2) + 64);
- $res .= $coded;
- }
- }
- $res =~ s/$/\$/;
-
- return $res;
-}
-
-my $av_preprocessor = 0;
-my $av_pending;
-my @av_paren_type;
-my $av_pend_colon;
-
-sub annotate_reset {
- $av_preprocessor = 0;
- $av_pending = '_';
- @av_paren_type = ('E');
- $av_pend_colon = 'O';
-}
-
-sub annotate_values {
- my ($stream, $type) = @_;
-
- my $res;
- my $var = '_' x length($stream);
- my $cur = $stream;
-
- print "$stream\n" if ($dbg_values > 1);
-
- while (length($cur)) {
- @av_paren_type = ('E') if ($#av_paren_type < 0);
- print " <" . join('', @av_paren_type) .
- "> <$type> <$av_pending>" if ($dbg_values > 1);
- if ($cur =~ /^(\s+)/o) {
- print "WS($1)\n" if ($dbg_values > 1);
- if ($1 =~ /\n/ && $av_preprocessor) {
- $type = pop(@av_paren_type);
- $av_preprocessor = 0;
- }
-
- } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') {
- print "CAST($1)\n" if ($dbg_values > 1);
- push(@av_paren_type, $type);
- $type = 'c';
-
- } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) {
- print "DECLARE($1)\n" if ($dbg_values > 1);
- $type = 'T';
-
- } elsif ($cur =~ /^($Modifier)\s*/) {
- print "MODIFIER($1)\n" if ($dbg_values > 1);
- $type = 'T';
-
- } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
- print "DEFINE($1,$2)\n" if ($dbg_values > 1);
- $av_preprocessor = 1;
- push(@av_paren_type, $type);
- if ($2 ne '') {
- $av_pending = 'N';
- }
- $type = 'E';
-
- } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) {
- print "UNDEF($1)\n" if ($dbg_values > 1);
- $av_preprocessor = 1;
- push(@av_paren_type, $type);
-
- } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) {
- print "PRE_START($1)\n" if ($dbg_values > 1);
- $av_preprocessor = 1;
-
- push(@av_paren_type, $type);
- push(@av_paren_type, $type);
- $type = 'E';
-
- } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) {
- print "PRE_RESTART($1)\n" if ($dbg_values > 1);
- $av_preprocessor = 1;
-
- push(@av_paren_type, $av_paren_type[$#av_paren_type]);
-
- $type = 'E';
-
- } elsif ($cur =~ /^(\#\s*(?:endif))/o) {
- print "PRE_END($1)\n" if ($dbg_values > 1);
-
- $av_preprocessor = 1;
-
- # Assume all arms of the conditional end as this
- # one does, and continue as if the #endif was not here.
- pop(@av_paren_type);
- push(@av_paren_type, $type);
- $type = 'E';
-
- } elsif ($cur =~ /^(\\\n)/o) {
- print "PRECONT($1)\n" if ($dbg_values > 1);
-
- } elsif ($cur =~ /^(__attribute__)\s*\(?/o) {
- print "ATTR($1)\n" if ($dbg_values > 1);
- $av_pending = $type;
- $type = 'N';
-
- } elsif ($cur =~ /^(sizeof)\s*(\()?/o) {
- print "SIZEOF($1)\n" if ($dbg_values > 1);
- if (defined $2) {
- $av_pending = 'V';
- }
- $type = 'N';
-
- } elsif ($cur =~ /^(if|while|for)\b/o) {
- print "COND($1)\n" if ($dbg_values > 1);
- $av_pending = 'E';
- $type = 'N';
-
- } elsif ($cur =~/^(case)/o) {
- print "CASE($1)\n" if ($dbg_values > 1);
- $av_pend_colon = 'C';
- $type = 'N';
-
- } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
- print "KEYWORD($1)\n" if ($dbg_values > 1);
- $type = 'N';
-
- } elsif ($cur =~ /^(\()/o) {
- print "PAREN('$1')\n" if ($dbg_values > 1);
- push(@av_paren_type, $av_pending);
- $av_pending = '_';
- $type = 'N';
-
- } elsif ($cur =~ /^(\))/o) {
- my $new_type = pop(@av_paren_type);
- if ($new_type ne '_') {
- $type = $new_type;
- print "PAREN('$1') -> $type\n"
- if ($dbg_values > 1);
- } else {
- print "PAREN('$1')\n" if ($dbg_values > 1);
- }
-
- } elsif ($cur =~ /^($Ident)\s*\(/o) {
- print "FUNC($1)\n" if ($dbg_values > 1);
- $type = 'V';
- $av_pending = 'V';
-
- } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
- if (defined $2 && $type eq 'C' || $type eq 'T') {
- $av_pend_colon = 'B';
- } elsif ($type eq 'E') {
- $av_pend_colon = 'L';
- }
- print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
- $type = 'V';
-
- } elsif ($cur =~ /^($Ident|$Constant)/o) {
- print "IDENT($1)\n" if ($dbg_values > 1);
- $type = 'V';
-
- } elsif ($cur =~ /^($Assignment)/o) {
- print "ASSIGN($1)\n" if ($dbg_values > 1);
- $type = 'N';
-
- } elsif ($cur =~/^(;|{|})/) {
- print "END($1)\n" if ($dbg_values > 1);
- $type = 'E';
- $av_pend_colon = 'O';
-
- } elsif ($cur =~/^(,)/) {
- print "COMMA($1)\n" if ($dbg_values > 1);
- $type = 'C';
-
- } elsif ($cur =~ /^(\?)/o) {
- print "QUESTION($1)\n" if ($dbg_values > 1);
- $type = 'N';
-
- } elsif ($cur =~ /^(:)/o) {
- print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
-
- substr($var, length($res), 1, $av_pend_colon);
- if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
- $type = 'E';
- } else {
- $type = 'N';
- }
- $av_pend_colon = 'O';
-
- } elsif ($cur =~ /^(\[)/o) {
- print "CLOSE($1)\n" if ($dbg_values > 1);
- $type = 'N';
-
- } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
- my $variant;
-
- print "OPV($1)\n" if ($dbg_values > 1);
- if ($type eq 'V') {
- $variant = 'B';
- } else {
- $variant = 'U';
- }
-
- substr($var, length($res), 1, $variant);
- $type = 'N';
-
- } elsif ($cur =~ /^($Operators)/o) {
- print "OP($1)\n" if ($dbg_values > 1);
- if ($1 ne '++' && $1 ne '--') {
- $type = 'N';
- }
-
- } elsif ($cur =~ /(^.)/o) {
- print "C($1)\n" if ($dbg_values > 1);
- }
- if (defined $1) {
- $cur = substr($cur, length($1));
- $res .= $type x length($1);
- }
- }
-
- return ($res, $var);
-}
-
-sub possible {
- my ($possible, $line) = @_;
- my $notPermitted = qr{(?:
- ^(?:
- $Modifier|
- $Storage|
- $Type|
- DEFINE_\S+
- )$|
- ^(?:
- goto|
- return|
- case|
- else|
- asm|__asm__|
- do|
- \#|
- \#\#|
- )(?:\s|$)|
- ^(?:typedef|struct|enum)\b
- )}x;
- warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
- if ($possible !~ $notPermitted) {
- # Check for modifiers.
- $possible =~ s/\s*$Storage\s*//g;
- $possible =~ s/\s*$Sparse\s*//g;
- if ($possible =~ /^\s*$/) {
-
- } elsif ($possible =~ /\s/) {
- $possible =~ s/\s*$Type\s*//g;
- for my $modifier (split(' ', $possible)) {
- if ($modifier !~ $notPermitted) {
- warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
- push(@modifierList, $modifier);
- }
- }
-
- } else {
- warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
- push(@typeList, $possible);
- }
- build_types();
- } else {
- warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
- }
-}
-
-my $prefix = '';
-
-sub show_type {
- my ($type) = @_;
-
- return defined $use_type{$type} if (scalar keys %use_type > 0);
-
- return !defined $ignore_type{$type};
-}
-
-sub report {
- my ($level, $type, $msg) = @_;
-
- if (!show_type($type) ||
- (defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
- return 0;
- }
- my $line;
- if ($show_types) {
- $line = "$prefix$level:$type: $msg\n";
- } else {
- $line = "$prefix$level: $msg\n";
- }
- $line = (split('\n', $line))[0] . "\n" if ($terse);
-
- push(our @report, $line);
-
- return 1;
-}
-
-sub report_dump {
- our @report;
-}
-
-sub ERROR {
- my ($type, $msg) = @_;
-
- if (report("ERROR", $type, $msg)) {
- our $clean = 0;
- our $cnt_error++;
- return 1;
- }
- return 0;
-}
-sub WARN {
- my ($type, $msg) = @_;
-
- if (report("WARNING", $type, $msg)) {
- our $clean = 0;
- our $cnt_warn++;
- return 1;
- }
- return 0;
-}
-sub CHK {
- my ($type, $msg) = @_;
-
- if ($check && report("CHECK", $type, $msg)) {
- our $clean = 0;
- our $cnt_chk++;
- return 1;
- }
- return 0;
-}
-
-sub trim {
- my ($string) = @_;
-
- $string =~ s/^\s+|\s+$//g;
-
- return $string;
-}
-
-sub ltrim {
- my ($string) = @_;
-
- $string =~ s/^\s+//;
-
- return $string;
-}
-
-sub rtrim {
- my ($string) = @_;
-
- $string =~ s/\s+$//;
-
- return $string;
-}
-
-sub string_find_replace {
- my ($string, $find, $replace) = @_;
-
- $string =~ s/$find/$replace/g;
-
- return $string;
-}
-
-sub tabify {
- my ($leading) = @_;
-
- my $source_indent = 8;
- my $max_spaces_before_tab = $source_indent - 1;
- my $spaces_to_tab = " " x $source_indent;
-
- #convert leading spaces to tabs
- 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g;
- #Remove spaces before a tab
- 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g;
-
- return "$leading";
-}
-
-sub pos_last_openparen {
- my ($line) = @_;
-
- my $pos = 0;
-
- my $opens = $line =~ tr/\(/\(/;
- my $closes = $line =~ tr/\)/\)/;
-
- my $last_openparen = 0;
-
- if (($opens == 0) || ($closes >= $opens)) {
- return -1;
- }
-
- my $len = length($line);
-
- for ($pos = 0; $pos < $len; $pos++) {
- my $string = substr($line, $pos);
- if ($string =~ /^($FuncArg|$balanced_parens)/) {
- $pos += length($1) - 1;
- } elsif (substr($line, $pos, 1) eq '(') {
- $last_openparen = $pos;
- } elsif (index($string, '(') == -1) {
- last;
- }
- }
-
- return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
-}
-
-sub process {
- my $filename = shift;
-
- my $linenr=0;
- my $prevline="";
- my $prevrawline="";
- my $stashline="";
- my $stashrawline="";
-
- my $length;
- my $indent;
- my $previndent=0;
- my $stashindent=0;
-
- our $clean = 1;
- my $signoff = 0;
- my $is_patch = 0;
-
- my $in_header_lines = 1;
- my $in_commit_log = 0; #Scanning lines before patch
-
- my $non_utf8_charset = 0;
-
- our @report = ();
- our $cnt_lines = 0;
- our $cnt_error = 0;
- our $cnt_warn = 0;
- our $cnt_chk = 0;
-
- # Trace the real file/line as we go.
- my $realfile = '';
- my $realline = 0;
- my $realcnt = 0;
- my $here = '';
- my $in_comment = 0;
- my $comment_edge = 0;
- my $first_line = 0;
- my $p1_prefix = '';
-
- my $prev_values = 'E';
-
- # suppression flags
- my %suppress_ifbraces;
- my %suppress_whiletrailers;
- my %suppress_export;
- my $suppress_statement = 0;
-
- my %signatures = ();
-
- # Pre-scan the patch sanitizing the lines.
-
- sanitise_line_reset();
- my $line;
- foreach my $rawline (@rawlines) {
- $linenr++;
- $line = $rawline;
-
- push(@fixed, $rawline) if ($fix);
-
- if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
- $realline=$1-1;
- if (defined $2) {
- $realcnt=$3+1;
- } else {
- $realcnt=1+1;
- }
- $in_comment = 0;
-
- # Guestimate if this is a continuing comment. Run
- # the context looking for a comment "edge". If this
- # edge is a close comment then we must be in a comment
- # at context start.
- my $edge;
- my $cnt = $realcnt;
- for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
- next if (defined $rawlines[$ln - 1] &&
- $rawlines[$ln - 1] =~ /^-/);
- $cnt--;
- #print "RAW<$rawlines[$ln - 1]>\n";
- last if (!defined $rawlines[$ln - 1]);
- if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
- $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
- ($edge) = $1;
- last;
- }
- }
- if (defined $edge && $edge eq '*/') {
- $in_comment = 1;
- }
-
- # Guestimate if this is a continuing comment. If this
- # is the start of a diff block and this line starts
- # ' *' then it is very likely a comment.
- if (!defined $edge &&
- $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
- {
- $in_comment = 1;
- }
-
- ##print "COMMENT:$in_comment edge<$edge> $rawline\n";
- sanitise_line_reset($in_comment);
-
- } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) {
- # Standardise the strings and chars within the input to
- # simplify matching -- only bother with positive lines.
- $line = sanitise_line($rawline);
- }
- push(@lines, $line);
-
- if ($realcnt > 1) {
- $realcnt-- if ($line =~ /^(?:\+| |$)/);
- } else {
- $realcnt = 0;
- }
-
- #print "==>$rawline\n";
- #print "-->$line\n";
- }
-
- $prefix = '';
-
- $realcnt = 0;
- $linenr = 0;
- foreach my $line (@lines) {
- $linenr++;
- my $sline = $line; #copy of $line
- $sline =~ s/$;/ /g; #with comments as spaces
-
- my $rawline = $rawlines[$linenr - 1];
-
-#extract the line range in the file after the patch is applied
- if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
- $is_patch = 1;
- $first_line = $linenr + 1;
- $realline=$1-1;
- if (defined $2) {
- $realcnt=$3+1;
- } else {
- $realcnt=1+1;
- }
- annotate_reset();
- $prev_values = 'E';
-
- %suppress_ifbraces = ();
- %suppress_whiletrailers = ();
- %suppress_export = ();
- $suppress_statement = 0;
- next;
-
-# track the line number as we move through the hunk, note that
-# new versions of GNU diff omit the leading space on completely
-# blank context lines so we need to count that too.
- } elsif ($line =~ /^( |\+|$)/) {
- $realline++;
- $realcnt-- if ($realcnt != 0);
-
- # Measure the line length and indent.
- ($length, $indent) = line_stats($rawline);
-
- # Track the previous line.
- ($prevline, $stashline) = ($stashline, $line);
- ($previndent, $stashindent) = ($stashindent, $indent);
- ($prevrawline, $stashrawline) = ($stashrawline, $rawline);
-
- #warn "line<$line>\n";
-
- } elsif ($realcnt == 1) {
- $realcnt--;
- }
-
- my $hunk_line = ($realcnt != 0);
-
-#make up the handle for any error we report on this line
- $prefix = "$filename:$realline: " if ($emacs && $file);
- $prefix = "$filename:$linenr: " if ($emacs && !$file);
-
- $here = "#$linenr: " if (!$file);
- $here = "#$realline: " if ($file);
-
- my $found_file = 0;
- # extract the filename as it passes
- if ($line =~ /^diff --git.*?(\S+)$/) {
- $realfile = $1;
- $realfile =~ s@^([^/]*)/@@ if (!$file);
- $in_commit_log = 0;
- $found_file = 1;
- } elsif ($line =~ /^\+\+\+\s+(\S+)/) {
- $realfile = $1;
- $realfile =~ s@^([^/]*)/@@ if (!$file);
- $in_commit_log = 0;
-
- $found_file = 1;
- }
-
- if ($found_file) {
- next;
- }
-
- $here .= "FILE: $realfile:$realline:" if ($realcnt != 0);
-
- my $hereline = "$here\n$rawline\n";
- my $herecurr = "$here\n$rawline\n";
- my $hereprev = "$here\n$prevrawline\n$rawline\n";
-
- $cnt_lines++ if ($realcnt != 0);
-
-# Check for incorrect file permissions
- if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
- my $permhere = $here . "FILE: $realfile\n";
- if ($realfile !~ m@scripts/@ &&
- $realfile !~ /\.(py|pl|awk|sh)$/) {
- ERROR("EXECUTE_PERMISSIONS",
- "do not set execute permissions for source files\n" . $permhere);
- }
- }
-
-# Check the patch for a signoff:
- if ($line =~ /^\s*signed-off-by:/i) {
- $signoff++;
- $in_commit_log = 0;
- }
-
-# Check signature styles
- if (!$in_header_lines &&
- $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) {
- my $space_before = $1;
- my $sign_off = $2;
- my $space_after = $3;
- my $email = $4;
- my $ucfirst_sign_off = ucfirst(lc($sign_off));
-
- if ($sign_off !~ /$signature_tags/) {
- WARN("BAD_SIGN_OFF",
- "Non-standard signature: $sign_off\n" . $herecurr);
- }
- if (defined $space_before && $space_before ne "") {
- if (WARN("BAD_SIGN_OFF",
- "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =
- "$ucfirst_sign_off $email";
- }
- }
- if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) {
- if (WARN("BAD_SIGN_OFF",
- "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =
- "$ucfirst_sign_off $email";
- }
-
- }
- if (!defined $space_after || $space_after ne " ") {
- if (WARN("BAD_SIGN_OFF",
- "Use a single space after $ucfirst_sign_off\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =
- "$ucfirst_sign_off $email";
- }
- }
-
- my ($email_name, $email_address, $comment) = parse_email($email);
- my $suggested_email = format_email(($email_name, $email_address));
- if ($suggested_email eq "") {
- ERROR("BAD_SIGN_OFF",
- "Unrecognized email address: '$email'\n" . $herecurr);
- } else {
- my $dequoted = $suggested_email;
- $dequoted =~ s/^"//;
- $dequoted =~ s/" </ </;
- # Don't force email to have quotes
- # Allow just an angle bracketed address
- if ("$dequoted$comment" ne $email &&
- "<$email_address>$comment" ne $email &&
- "$suggested_email$comment" ne $email) {
- WARN("BAD_SIGN_OFF",
- "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr);
- }
- }
-
-# Check for duplicate signatures
- my $sig_nospace = $line;
- $sig_nospace =~ s/\s//g;
- $sig_nospace = lc($sig_nospace);
- if (defined $signatures{$sig_nospace}) {
- WARN("BAD_SIGN_OFF",
- "Duplicate signature\n" . $herecurr);
- } else {
- $signatures{$sig_nospace} = 1;
- }
- }
-
-# Check for unwanted Gerrit info
- if ($in_commit_log && $line =~ /^\s*change-id:/i) {
- ERROR("GERRIT_CHANGE_ID",
- "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr);
- }
-
-# Check for wrappage within a valid hunk of the file
- if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
- ERROR("CORRUPTED_PATCH",
- "patch seems to be corrupt (line wrapped?)\n" .
- $herecurr) if (!$emitted_corrupt++);
- }
-
-# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
- if (($realfile =~ /^$/ || $line =~ /^\+/) &&
- $rawline !~ m/^$UTF8*$/) {
- my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/);
-
- my $blank = copy_spacing($rawline);
- my $ptr = substr($blank, 0, length($utf8_prefix)) . "^";
- my $hereptr = "$hereline$ptr\n";
-
- CHK("INVALID_UTF8",
- "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
- }
-
-# Check if it's the start of a commit log
-# (not a header line and we haven't seen the patch filename)
- if ($in_header_lines && $realfile =~ /^$/ &&
- $rawline !~ /^(commit\b|from\b|[\w-]+:).+$/i) {
- $in_header_lines = 0;
- $in_commit_log = 1;
- }
-
-# Check if there is UTF-8 in a commit log when a mail header has explicitly
-# declined it, i.e defined some charset where it is missing.
- if ($in_header_lines &&
- $rawline =~ /^Content-Type:.+charset="(.+)".*$/ &&
- $1 !~ /utf-8/i) {
- $non_utf8_charset = 1;
- }
-
- if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ &&
- $rawline =~ /$NON_ASCII_UTF8/) {
- WARN("UTF8_BEFORE_PATCH",
- "8-bit UTF-8 used in possible commit log\n" . $herecurr);
- }
-
-# ignore non-hunk lines and lines being removed
- next if (!$hunk_line || $line =~ /^-/);
-
-#trailing whitespace
- if ($line =~ /^\+.*\015/) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- if (ERROR("DOS_LINE_ENDINGS",
- "DOS line endings\n" . $herevet) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/[\s\015]+$//;
- }
- } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- if (ERROR("TRAILING_WHITESPACE",
- "trailing whitespace\n" . $herevet) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/\s+$//;
- }
-
- $rpt_cleaners = 1;
- }
-
-# Check for FSF mailing addresses.
- if ($rawline =~ /\bwrite to the Free/i ||
- $rawline =~ /\b59\s+Temple\s+Pl/i ||
- $rawline =~ /\b51\s+Franklin\s+St/i) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- my $msg_type = \&ERROR;
- $msg_type = \&CHK if ($file);
- &{$msg_type}("FSF_MAILING_ADDRESS",
- "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. This source tree already includes a copy of the GPL.\n" . $herevet)
- }
-
-# check we are in a valid source file if not then ignore this hunk
- next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/);
-
-#line length limit
- if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ &&
- $rawline !~ /^.\s*\*\s*\@$Ident\s/ &&
- !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:|,|\)\s*;)\s*$/ ||
- $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) &&
- $length > $max_line_length)
- {
- WARN("LONG_LINE",
- "line over $max_line_length characters\n" . $herecurr);
- }
-
-# Check for user-visible strings broken across lines, which breaks the ability
-# to grep for the string. Make exceptions when the previous string ends in a
-# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{'
-# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value
- if ($line =~ /^\+\s*"/ &&
- $prevline =~ /"\s*$/ &&
- $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) {
- WARN("SPLIT_STRING",
- "quoted string split across lines\n" . $hereprev);
- }
-
-# check for spaces before a quoted newline
- if ($rawline =~ /^.*\".*\s\\n/) {
- if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
- "unnecessary whitespace before a quoted newline\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/^(\+.*\".*)\s+\\n/$1\\n/;
- }
-
- }
-
-# check for adding lines without a newline.
- if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
- WARN("MISSING_EOF_NEWLINE",
- "adding a line without newline at end of file\n" . $herecurr);
- }
-
-# check we are in a valid source file C or perl if not then ignore this hunk
- next if ($realfile !~ /\.(h|c|pl)$/);
-
- if ($hardtabs) {
-# at the beginning of a line any tabs must come first and anything
-# more than 8 must use tabs.
- if ($rawline =~ /^\+\s* \t\s*\S/ ||
- $rawline =~ /^\+\s* \s*/) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- $rpt_cleaners = 1;
- if (ERROR("CODE_INDENT",
- "code indent should use tabs where possible\n" . $herevet) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
- }
- }
-
-# check for space before tabs.
- if ($rawline =~ /^\+/ && $rawline =~ / \t/) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- if (WARN("SPACE_BEFORE_TAB",
- "please, no space before tabs\n" . $herevet) &&
- $fix) {
- while ($fixed[$linenr - 1] =~
- s/(^\+.*) {8,8}\t/$1\t\t/) {}
- while ($fixed[$linenr - 1] =~
- s/(^\+.*) +\t/$1\t/) {}
- }
- }
- } else {
-# check for tabs at the beginning of a line.
- if ($rawline =~ /^\+\s*\t/) {
- my $herevet = "$here\n" . $rawline . "\n";
- ERROR("CODE_INDENT",
- "code indent should use space instead of tabs\n" . $herevet);
- }
- }
-
-# check for && or || at the start of a line
- if ($rawline =~ /^\+\s*(&&|\|\|)/) {
- CHK("LOGICAL_CONTINUATIONS",
- "Logical continuations should be on the previous line\n" . $hereprev);
- }
-
-# check multi-line statement indentation matches previous line
- if ($^V && $^V ge 5.10.0 &&
- $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|$Ident\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
- $prevline =~ /^\+(\t*)(.*)$/;
- my $oldindent = $1;
- my $rest = $2;
-
- my $pos = pos_last_openparen($rest);
- if ($pos >= 0) {
- $line =~ /^(\+| )([ \t]*)/;
- my $newindent = $2;
-
- my $goodtabindent = $oldindent .
- "\t" x ($pos / 8) .
- " " x ($pos % 8);
- my $goodspaceindent = $oldindent . " " x $pos;
-
- if ($newindent ne $goodtabindent &&
- $newindent ne $goodspaceindent) {
-
- if (CHK("PARENTHESIS_ALIGNMENT",
- "Alignment should match open parenthesis\n" . $hereprev) &&
- $fix && $line =~ /^\+/) {
- $fixed[$linenr - 1] =~
- s/^\+[ \t]*/\+$goodtabindent/;
- }
- }
- }
- }
-
- if ($line =~ /^\+.*\*[ \t]*\)[ \t]+(?!$Assignment|$Arithmetic)/) {
- if (CHK("SPACING",
- "No space is necessary after a cast\n" . $hereprev) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/^(\+.*\*[ \t]*\))[ \t]+/$1/;
- }
- }
-
-# check for missing blank lines after declarations
- if ($sline =~ /^\+\s+\S/ && #Not at char 1
- # actual declarations
- ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
- # foo bar; where foo is some local typedef or #define
- $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/) &&
- # for "else if" which can look like "$Ident $Ident"
- !($prevline =~ /^\+\s+$c90_Keywords\b/ ||
- # other possible extensions of declaration lines
- $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
- # not starting a section or a macro "\" extended line
- $prevline =~ /(?:\{\s*|\\)$/) &&
- # looks like a declaration
- !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
- # foo bar; where foo is some local typedef or #define
- $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
- # start of struct or union or enum
- $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ ||
- # start or end of block or continuation of declaration
- $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
- # bitfield continuation
- $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
- # other possible extensions of declaration lines
- $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) &&
- # indentation of previous and current line are the same
- (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) {
- WARN("SPACING",
- "Missing a blank line after declarations\n" . $hereprev);
- }
-
- if ($hardtabs) {
-# check for spaces at the beginning of a line.
-# Exceptions:
-# 1) within comments
-# 2) indented preprocessor commands
-# 3) hanging labels
- if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- if (WARN("LEADING_SPACE",
- "please, no spaces at the start of a line\n" . $herevet) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
- }
- }
- }
-
-# check we are in a valid C source file if not then ignore this hunk
- next if ($realfile !~ /\.(h|c)$/);
-
-# check for RCS/CVS revision markers
- if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
- WARN("CVS_KEYWORD",
- "CVS style keyword markers, these will _not_ be updated\n". $herecurr);
- }
-
-# Check for potential 'bare' types
- my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
- $realline_next);
-#print "LINE<$line>\n";
- if ($linenr >= $suppress_statement &&
- $realcnt && $sline =~ /.\s*\S/) {
- ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
- ctx_statement_block($linenr, $realcnt, 0);
- $stat =~ s/\n./\n /g;
- $cond =~ s/\n./\n /g;
-
-#print "linenr<$linenr> <$stat>\n";
- # If this statement has no statement boundaries within
- # it there is no point in retrying a statement scan
- # until we hit end of it.
- my $frag = $stat; $frag =~ s/;+\s*$//;
- if ($frag !~ /(?:{|;)/) {
-#print "skip<$line_nr_next>\n";
- $suppress_statement = $line_nr_next;
- }
-
- # Find the real next line.
- $realline_next = $line_nr_next;
- if (defined $realline_next &&
- (!defined $lines[$realline_next - 1] ||
- substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) {
- $realline_next++;
- }
-
- my $s = $stat;
- $s =~ s/{.*$//s;
-
- # Ignore goto labels.
- if ($s =~ /$Ident:\*$/s) {
-
- # Ignore functions being called
- } elsif ($s =~ /^.\s*$Ident\s*\(/s) {
-
- } elsif ($s =~ /^.\s*else\b/s) {
-
- # declarations always start with types
- } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
- my $type = $1;
- $type =~ s/\s+/ /g;
- possible($type, "A:" . $s);
-
- # definitions in global scope can only start with types
- } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) {
- possible($1, "B:" . $s);
- }
-
- # any (foo ... *) is a pointer cast, and foo is a type
- while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) {
- possible($1, "C:" . $s);
- }
-
- # Check for any sort of function declaration.
- # int foo(something bar, other baz);
- # void (*store_gdt)(x86_descr_ptr *);
- if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) {
- my ($name_len) = length($1);
-
- my $ctx = $s;
- substr($ctx, 0, $name_len + 1, '');
- $ctx =~ s/\)[^\)]*$//;
-
- for my $arg (split(/\s*,\s*/, $ctx)) {
- if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) {
-
- possible($1, "D:" . $s);
- }
- }
- }
-
- }
-
-#
-# Checks which may be anchored in the context.
-#
-
-# Check for switch () and associated case and default
-# statements should be at the same indent.
- if ($line=~/\bswitch\s*\(.*\)/) {
- my $err = '';
- my $sep = '';
- my @ctx = ctx_block_outer($linenr, $realcnt);
- shift(@ctx);
- for my $ctx (@ctx) {
- my ($clen, $cindent) = line_stats($ctx);
- if ($ctx =~ /^\+\s*(case\s+|default:)/ &&
- $indent != $cindent) {
- $err .= "$sep$ctx\n";
- $sep = '';
- } else {
- $sep = "[...]\n";
- }
- }
- if ($err ne '') {
- ERROR("SWITCH_CASE_INDENT_LEVEL",
- "switch and case should be at the same indent\n$hereline$err");
- }
- }
-
-# if/while/etc brace do not go on next line, unless defining a do while loop,
-# or if that brace on the next line is for something else
- if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
- my $pre_ctx = "$1$2";
-
- my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
-
- if ($line =~ /^\+\t{6,}/) {
- WARN("DEEP_INDENTATION",
- "Too many leading tabs - consider code refactoring\n" . $herecurr);
- }
-
- my $ctx_cnt = $realcnt - $#ctx - 1;
- my $ctx = join("\n", @ctx);
-
- my $ctx_ln = $linenr;
- my $ctx_skip = $realcnt;
-
- while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt &&
- defined $lines[$ctx_ln - 1] &&
- $lines[$ctx_ln - 1] =~ /^-/)) {
- ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n";
- $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/);
- $ctx_ln++;
- }
-
- #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
- #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
-
- if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) {
- ERROR("OPEN_BRACE",
- "that open brace { should be on the previous line\n" .
- "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
- }
- if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ &&
- $ctx =~ /\)\s*\;\s*$/ &&
- defined $lines[$ctx_ln - 1])
- {
- my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]);
- if ($nindent > $indent) {
- WARN("TRAILING_SEMICOLON",
- "trailing semicolon indicates no statements, indent implies otherwise\n" .
- "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
- }
- }
- }
-
-# Check relative indent for conditionals and blocks.
- if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
- ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
- ctx_statement_block($linenr, $realcnt, 0)
- if (!defined $stat);
- my ($s, $c) = ($stat, $cond);
-
- substr($s, 0, length($c), '');
-
- # Make sure we remove the line prefixes as we have
- # none on the first line, and are going to readd them
- # where necessary.
- $s =~ s/\n./\n/gs;
-
- # Find out how long the conditional actually is.
- my @newlines = ($c =~ /\n/gs);
- my $cond_lines = 1 + $#newlines;
-
- # We want to check the first line inside the block
- # starting at the end of the conditional, so remove:
- # 1) any blank line termination
- # 2) any opening brace { on end of the line
- # 3) any do (...) {
- my $continuation = 0;
- my $check = 0;
- $s =~ s/^.*\bdo\b//;
- $s =~ s/^\s*{//;
- if ($s =~ s/^\s*\\//) {
- $continuation = 1;
- }
- if ($s =~ s/^\s*?\n//) {
- $check = 1;
- $cond_lines++;
- }
-
- # Also ignore a loop construct at the end of a
- # preprocessor statement.
- if (($prevline =~ /^.\s*#\s*define\s/ ||
- $prevline =~ /\\\s*$/) && $continuation == 0) {
- $check = 0;
- }
-
- my $cond_ptr = -1;
- $continuation = 0;
- while ($cond_ptr != $cond_lines) {
- $cond_ptr = $cond_lines;
-
- # If we see an #else/#elif then the code
- # is not linear.
- if ($s =~ /^\s*\#\s*(?:else|elif)/) {
- $check = 0;
- }
-
- # Ignore:
- # 1) blank lines, they should be at 0,
- # 2) preprocessor lines, and
- # 3) labels.
- if ($continuation ||
- $s =~ /^\s*?\n/ ||
- $s =~ /^\s*#\s*?/ ||
- $s =~ /^\s*$Ident\s*:/) {
- $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
- if ($s =~ s/^.*?\n//) {
- $cond_lines++;
- }
- }
- }
-
- my (undef, $sindent) = line_stats("+" . $s);
- my $stat_real = raw_line($linenr, $cond_lines);
-
- # Check if either of these lines are modified, else
- # this is not this patch's fault.
- if (!defined($stat_real) ||
- $stat !~ /^\+/ && $stat_real !~ /^\+/) {
- $check = 0;
- }
- if (defined($stat_real) && $cond_lines > 1) {
- $stat_real = "[...]\n$stat_real";
- }
-
- #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
-
- if ($check && (($sindent % 8) != 0 ||
- ($sindent <= $indent && $s ne ''))) {
- WARN("SUSPECT_CODE_INDENT",
- "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
- }
- }
-
- # Track the 'values' across context and added lines.
- my $opline = $line; $opline =~ s/^./ /;
- my ($curr_values, $curr_vars) =
- annotate_values($opline . "\n", $prev_values);
- $curr_values = $prev_values . $curr_values;
- if ($dbg_values) {
- my $outline = $opline; $outline =~ s/\t/ /g;
- print "$linenr > .$outline\n";
- print "$linenr > $curr_values\n";
- print "$linenr > $curr_vars\n";
- }
- $prev_values = substr($curr_values, -1);
-
-#ignore lines not being added
- next if ($line =~ /^[^\+]/);
-
-# TEST: allow direct testing of the type matcher.
- if ($dbg_type) {
- if ($line =~ /^.\s*$Declare\s*$/) {
- ERROR("TEST_TYPE",
- "TEST: is type\n" . $herecurr);
- } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) {
- ERROR("TEST_NOT_TYPE",
- "TEST: is not type ($1 is)\n". $herecurr);
- }
- next;
- }
-# TEST: allow direct testing of the attribute matcher.
- if ($dbg_attr) {
- if ($line =~ /^.\s*$Modifier\s*$/) {
- ERROR("TEST_ATTR",
- "TEST: is attr\n" . $herecurr);
- } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) {
- ERROR("TEST_NOT_ATTR",
- "TEST: is not attr ($1 is)\n". $herecurr);
- }
- next;
- }
-
-# check for initialisation to aggregates open brace on the next line
- if ($line =~ /^.\s*{/ &&
- $prevline =~ /(?:^|[^=])=\s*$/) {
- ERROR("OPEN_BRACE",
- "that open brace { should be on the previous line\n" . $hereprev);
- }
-
-#
-# Checks which are anchored on the added line.
-#
-
-# check for malformed paths in #include statements (uses RAW line)
- if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) {
- my $path = $1;
- if ($path =~ m{//}) {
- ERROR("MALFORMED_INCLUDE",
- "malformed #include filename\n" . $herecurr);
- }
- if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) {
- ERROR("UAPI_INCLUDE",
- "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr);
- }
- }
-
-# no C99 // comments
- if ($line =~ m{//}) {
- if (ERROR("C99_COMMENTS",
- "do not use C99 // comments\n" . $herecurr) &&
- $fix) {
- my $line = $fixed[$linenr - 1];
- if ($line =~ /\/\/(.*)$/) {
- my $comment = trim($1);
- $fixed[$linenr - 1] =~ s@\/\/(.*)$@/\* $comment \*/@;
- }
- }
- }
- # Remove C99 comments.
- $line =~ s@//.*@@;
- $opline =~ s@//.*@@;
-
-# check for global initialisers.
- if ($line =~ /^\+(\s*$Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/) {
- if (ERROR("GLOBAL_INITIALISERS",
- "do not initialise globals to 0 or NULL\n" .
- $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/($Type\s*$Ident\s*(?:\s+$Modifier))*\s*=\s*(0|NULL|false)\s*;/$1;/;
- }
- }
-# check for static initialisers.
- if ($line =~ /^\+.*\bstatic\s.*=\s*(0|NULL|false)\s*;/) {
- if (ERROR("INITIALISED_STATIC",
- "do not initialise statics to 0 or NULL\n" .
- $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/(\bstatic\s.*?)\s*=\s*(0|NULL|false)\s*;/$1;/;
- }
- }
-
-# check for static const char * arrays.
- if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) {
- WARN("STATIC_CONST_CHAR_ARRAY",
- "static const char * array should probably be static const char * const\n" .
- $herecurr);
- }
-
-# check for static char foo[] = "bar" declarations.
- if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) {
- WARN("STATIC_CONST_CHAR_ARRAY",
- "static char array declaration should probably be static const char\n" .
- $herecurr);
- }
-
-# check for non-global char *foo[] = {"bar", ...} declarations.
- if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
- WARN("STATIC_CONST_CHAR_ARRAY",
- "char * array declaration might be better as static const\n" .
- $herecurr);
- }
-
-# check for function declarations without arguments like "int foo()"
- if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) {
- if (ERROR("FUNCTION_WITHOUT_ARGS",
- "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/;
- }
- }
-
-# check for new typedefs, only function parameters and sparse annotations
-# make sense.
- if ($line =~ /\btypedef\s/ &&
- $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ &&
- $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ &&
- $line !~ /\b$typeTypedefs\b/ &&
- $line !~ /\b__bitwise(?:__|)\b/) {
- WARN("NEW_TYPEDEFS",
- "do not add new typedefs\n" . $herecurr);
- }
-
-# * goes on variable not on type
- # (char*[ const])
- while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) {
- #print "AA<$1>\n";
- my ($ident, $from, $to) = ($1, $2, $2);
-
- # Should start with a space.
- $to =~ s/^(\S)/ $1/;
- # Should not end with a space.
- $to =~ s/\s+$//;
- # '*'s should not have spaces between.
- while ($to =~ s/\*\s+\*/\*\*/) {
- }
-
-## print "1: from<$from> to<$to> ident<$ident>\n";
- if ($from ne $to) {
- if (ERROR("POINTER_LOCATION",
- "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) &&
- $fix) {
- my $sub_from = $ident;
- my $sub_to = $ident;
- $sub_to =~ s/\Q$from\E/$to/;
- $fixed[$linenr - 1] =~
- s@\Q$sub_from\E@$sub_to@;
- }
- }
- }
- while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) {
- #print "BB<$1>\n";
- my ($match, $from, $to, $ident) = ($1, $2, $2, $3);
-
- # Should start with a space.
- $to =~ s/^(\S)/ $1/;
- # Should not end with a space.
- $to =~ s/\s+$//;
- # '*'s should not have spaces between.
- while ($to =~ s/\*\s+\*/\*\*/) {
- }
- # Modifiers should have spaces.
- $to =~ s/(\b$Modifier$)/$1 /;
-
-## print "2: from<$from> to<$to> ident<$ident>\n";
- if ($from ne $to && $ident !~ /^$Modifier$/) {
- if (ERROR("POINTER_LOCATION",
- "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) &&
- $fix) {
-
- my $sub_from = $match;
- my $sub_to = $match;
- $sub_to =~ s/\Q$from\E/$to/;
- $fixed[$linenr - 1] =~
- s@\Q$sub_from\E@$sub_to@;
- }
- }
- }
-
-# function brace can't be on same line, except for #defines of do while,
-# or if closed on same line
- if (($line=~/$Type\s*$Ident\(.*\).*\s\{/) and
- !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) {
- ERROR("OPEN_BRACE",
- "open brace '{' following function declarations go on the next line\n" . $herecurr);
- }
-
-# open braces for enum, union and struct go on the same line.
- if ($line =~ /^.\s*{/ &&
- $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) {
- ERROR("OPEN_BRACE",
- "open brace '{' following $1 go on the same line\n" . $hereprev);
- }
-
-# missing space after union, struct or enum definition
- if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) {
- if (WARN("SPACING",
- "missing space after $1 definition\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/;
- }
- }
-
-# Function pointer declarations
-# check spacing between type, funcptr, and args
-# canonical declaration is "type (*funcptr)(args...)"
- if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) {
- my $declare = $1;
- my $pre_pointer_space = $2;
- my $post_pointer_space = $3;
- my $funcname = $4;
- my $post_funcname_space = $5;
- my $pre_args_space = $6;
-
-# the $Declare variable will capture all spaces after the type
-# so check it for a missing trailing missing space but pointer return types
-# don't need a space so don't warn for those.
- my $post_declare_space = "";
- if ($declare =~ /(\s+)$/) {
- $post_declare_space = $1;
- $declare = rtrim($declare);
- }
- if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) {
- WARN("SPACING",
- "missing space after return type\n" . $herecurr);
- $post_declare_space = " ";
- }
-
-# unnecessary space "type (*funcptr)(args...)"
-# This test is not currently implemented because these declarations are
-# equivalent to
-# int foo(int bar, ...)
-# and this is form shouldn't/doesn't generate a checkpatch warning.
-#
-# elsif ($declare =~ /\s{2,}$/) {
-# WARN("SPACING",
-# "Multiple spaces after return type\n" . $herecurr);
-# }
-
-# unnecessary space "type ( *funcptr)(args...)"
- if (defined $pre_pointer_space &&
- $pre_pointer_space =~ /^\s/) {
- WARN("SPACING",
- "Unnecessary space after function pointer open parenthesis\n" . $herecurr);
- }
-
-# unnecessary space "type (* funcptr)(args...)"
- if (defined $post_pointer_space &&
- $post_pointer_space =~ /^\s/) {
- WARN("SPACING",
- "Unnecessary space before function pointer name\n" . $herecurr);
- }
-
-# unnecessary space "type (*funcptr )(args...)"
- if (defined $post_funcname_space &&
- $post_funcname_space =~ /^\s/) {
- WARN("SPACING",
- "Unnecessary space after function pointer name\n" . $herecurr);
- }
-
-# unnecessary space "type (*funcptr) (args...)"
- if (defined $pre_args_space &&
- $pre_args_space =~ /^\s/) {
- WARN("SPACING",
- "Unnecessary space before function pointer arguments\n" . $herecurr);
- }
-
- if (show_type("SPACING") && $fix) {
- $fixed[$linenr - 1] =~
- s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex;
- }
- }
-
-# check for spacing round square brackets; allowed:
-# 1. with a type on the left -- int [] a;
-# 2. at the beginning of a line for slice initialisers -- [0...10] = 5,
-# 3. inside a curly brace -- = { [0...10] = 5 }
- while ($line =~ /(.*?\s)\[/g) {
- my ($where, $prefix) = ($-[1], $1);
- if ($prefix !~ /$Type\s+$/ &&
- ($where != 0 || $prefix !~ /^.\s+$/) &&
- $prefix !~ /[{,]\s+$/) {
- if (ERROR("BRACKET_SPACE",
- "space prohibited before open square bracket '['\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/^(\+.*?)\s+\[/$1\[/;
- }
- }
- }
-
-# check for spaces between functions and their parentheses.
- while ($line =~ /($Ident)\s+\(/g) {
- my $name = $1;
- my $ctx_before = substr($line, 0, $-[1]);
- my $ctx = "$ctx_before$name";
-
- # Ignore those directives where spaces _are_ permitted.
- if ($name =~ /^(?:
- if|for|while|switch|return|case|
- volatile|__volatile__|
- __attribute__|format|__extension__|
- asm|__asm__)$/x)
- {
- # cpp #define statements have non-optional spaces, ie
- # if there is a space between the name and the open
- # parenthesis it is simply not a parameter group.
- } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) {
-
- # cpp #elif statement condition may start with a (
- } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) {
-
- # If this whole things ends with a type its most
- # likely a typedef for a function.
- } elsif ($ctx =~ /$Type$/) {
-
- } else {
- if (WARN("SPACING",
- "space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/\b$name\s+\(/$name\(/;
- }
- }
- }
-
-# Check operator spacing.
- if (!($line=~/\#\s*include/)) {
- my $fixed_line = "";
- my $line_fixed = 0;
-
- my $ops = qr{
- <<=|>>=|<=|>=|==|!=|
- \+=|-=|\*=|\/=|%=|\^=|\|=|&=|
- =>|->|<<|>>|<|>|=|!|~|
- &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
- \?:|\?|:
- }x;
- my @elements = split(/($ops|;)/, $opline);
-
-## print("element count: <" . $#elements . ">\n");
-## foreach my $el (@elements) {
-## print("el: <$el>\n");
-## }
-
- my @fix_elements = ();
- my $off = 0;
-
- foreach my $el (@elements) {
- push(@fix_elements, substr($rawline, $off, length($el)));
- $off += length($el);
- }
-
- $off = 0;
-
- my $blank = copy_spacing($opline);
- my $last_after = -1;
-
- for (my $n = 0; $n < $#elements; $n += 2) {
-
- my $good = $fix_elements[$n] . $fix_elements[$n + 1];
-
-## print("n: <$n> good: <$good>\n");
-
- $off += length($elements[$n]);
-
- # Pick up the preceding and succeeding characters.
- my $ca = substr($opline, 0, $off);
- my $cc = '';
- if (length($opline) >= ($off + length($elements[$n + 1]))) {
- $cc = substr($opline, $off + length($elements[$n + 1]));
- }
- my $cb = "$ca$;$cc";
-
- my $a = '';
- $a = 'V' if ($elements[$n] ne '');
- $a = 'W' if ($elements[$n] =~ /\s$/);
- $a = 'C' if ($elements[$n] =~ /$;$/);
- $a = 'B' if ($elements[$n] =~ /(\[|\()$/);
- $a = 'O' if ($elements[$n] eq '');
- $a = 'E' if ($ca =~ /^\s*$/);
-
- my $op = $elements[$n + 1];
-
- my $c = '';
- if (defined $elements[$n + 2]) {
- $c = 'V' if ($elements[$n + 2] ne '');
- $c = 'W' if ($elements[$n + 2] =~ /^\s/);
- $c = 'C' if ($elements[$n + 2] =~ /^$;/);
- $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/);
- $c = 'O' if ($elements[$n + 2] eq '');
- $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/);
- } else {
- $c = 'E';
- }
-
- my $ctx = "${a}x${c}";
-
- my $at = "(ctx:$ctx)";
-
- my $ptr = substr($blank, 0, $off) . "^";
- my $hereptr = "$hereline$ptr\n";
-
- # Pull out the value of this operator.
- my $op_type = substr($curr_values, $off + 1, 1);
-
- # Get the full operator variant.
- my $opv = $op . substr($curr_vars, $off, 1);
-
- # Ignore operators passed as parameters.
- if ($op_type ne 'V' &&
- $ca =~ /\s$/ && $cc =~ /^\s*,/) {
-
-# # Ignore comments
-# } elsif ($op =~ /^$;+$/) {
-
- # ; should have either the end of line or a space or \ after it
- } elsif ($op eq ';') {
- if ($ctx !~ /.x[WEBC]/ &&
- $cc !~ /^\\/ && $cc !~ /^;/) {
- if (ERROR("SPACING",
- "space required after that '$op' $at\n" . $hereptr)) {
- $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
- $line_fixed = 1;
- }
- }
-
- # // is a comment
- } elsif ($op eq '//') {
-
- # : when part of a bitfield
- } elsif ($opv eq ':B') {
- # skip the bitfield test for now
-
- # No spaces for:
- # ->
- } elsif ($op eq '->') {
- if ($ctx =~ /Wx.|.xW/) {
- if (ERROR("SPACING",
- "spaces prohibited around that '$op' $at\n" . $hereptr)) {
- $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
- if (defined $fix_elements[$n + 2]) {
- $fix_elements[$n + 2] =~ s/^\s+//;
- }
- $line_fixed = 1;
- }
- }
-
- # , must have a space on the right.
- } elsif ($op eq ',') {
- if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) {
- if (ERROR("SPACING",
- "space required after that '$op' $at\n" . $hereptr)) {
- $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
- $line_fixed = 1;
- $last_after = $n;
- }
- }
-
- # '*' as part of a type definition -- reported already.
- } elsif ($opv eq '*_') {
- #warn "'*' is part of type\n";
-
- # unary operators should have a space before and
- # none after. May be left adjacent to another
- # unary operator, or a cast
- } elsif ($op eq '!' || $op eq '~' ||
- $opv eq '*U' || $opv eq '-U' ||
- $opv eq '&U' || $opv eq '&&U') {
- if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) {
- if (ERROR("SPACING",
- "space required before that '$op' $at\n" . $hereptr)) {
- if ($n != $last_after + 2) {
- $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]);
- $line_fixed = 1;
- }
- }
- }
- if ($op eq '*' && $cc =~/\s*$Modifier\b/) {
- # A unary '*' may be const
-
- } elsif ($ctx =~ /.xW/) {
- if (ERROR("SPACING",
- "space prohibited after that '$op' $at\n" . $hereptr)) {
- $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]);
- if (defined $fix_elements[$n + 2]) {
- $fix_elements[$n + 2] =~ s/^\s+//;
- }
- $line_fixed = 1;
- }
- }
-
- # unary ++ and unary -- are allowed no space on one side.
- } elsif ($op eq '++' or $op eq '--') {
- if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) {
- if (ERROR("SPACING",
- "space required one side of that '$op' $at\n" . $hereptr)) {
- $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
- $line_fixed = 1;
- }
- }
- if ($ctx =~ /Wx[BE]/ ||
- ($ctx =~ /Wx./ && $cc =~ /^;/)) {
- if (ERROR("SPACING",
- "space prohibited before that '$op' $at\n" . $hereptr)) {
- $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
- $line_fixed = 1;
- }
- }
- if ($ctx =~ /ExW/) {
- if (ERROR("SPACING",
- "space prohibited after that '$op' $at\n" . $hereptr)) {
- $good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
- if (defined $fix_elements[$n + 2]) {
- $fix_elements[$n + 2] =~ s/^\s+//;
- }
- $line_fixed = 1;
- }
- }
-
- # << and >> may either have or not have spaces both sides
- } elsif ($op eq '<<' or $op eq '>>' or
- $op eq '&' or $op eq '^' or $op eq '|' or
- $op eq '+' or $op eq '-' or
- $op eq '*' or $op eq '/' or
- $op eq '%')
- {
- if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) {
- if (ERROR("SPACING",
- "need consistent spacing around '$op' $at\n" . $hereptr)) {
- $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
- if (defined $fix_elements[$n + 2]) {
- $fix_elements[$n + 2] =~ s/^\s+//;
- }
- $line_fixed = 1;
- }
- }
-
- # A colon needs no spaces before when it is
- # terminating a case value or a label.
- } elsif ($opv eq ':C' || $opv eq ':L') {
- if ($ctx =~ /Wx./) {
- if (ERROR("SPACING",
- "space prohibited before that '$op' $at\n" . $hereptr)) {
- $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
- $line_fixed = 1;
- }
- }
-
- # All the others need spaces both sides.
- } elsif ($ctx !~ /[EWC]x[CWE]/) {
- my $ok = 0;
-
- # Ignore email addresses <foo@bar>
- if (($op eq '<' &&
- $cc =~ /^\S+\@\S+>/) ||
- ($op eq '>' &&
- $ca =~ /<\S+\@\S+$/))
- {
- $ok = 1;
- }
-
- # messages are ERROR, but ?: are CHK
- if ($ok == 0) {
- my $msg_type = \&ERROR;
- $msg_type = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/);
-
- if (&{$msg_type}("SPACING",
- "spaces required around that '$op' $at\n" . $hereptr)) {
- $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
- if (defined $fix_elements[$n + 2]) {
- $fix_elements[$n + 2] =~ s/^\s+//;
- }
- $line_fixed = 1;
- }
- }
- }
- $off += length($elements[$n + 1]);
-
-## print("n: <$n> GOOD: <$good>\n");
-
- $fixed_line = $fixed_line . $good;
- }
-
- if (($#elements % 2) == 0) {
- $fixed_line = $fixed_line . $fix_elements[$#elements];
- }
-
- if ($fix && $line_fixed && $fixed_line ne $fixed[$linenr - 1]) {
- $fixed[$linenr - 1] = $fixed_line;
- }
-
-
- }
-
-# check for whitespace before a non-naked semicolon
- if ($line =~ /^\+.*\S\s+;\s*$/) {
- if (WARN("SPACING",
- "space prohibited before semicolon\n" . $herecurr) &&
- $fix) {
- 1 while $fixed[$linenr - 1] =~
- s/^(\+.*\S)\s+;/$1;/;
- }
- }
-
-# check for multiple assignments
- if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) {
- CHK("MULTIPLE_ASSIGNMENTS",
- "multiple assignments should be avoided\n" . $herecurr);
- }
-
-## # check for multiple declarations, allowing for a function declaration
-## # continuation.
-## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ &&
-## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
-##
-## # Remove any bracketed sections to ensure we do not
-## # falsly report the parameters of functions.
-## my $ln = $line;
-## while ($ln =~ s/\([^\(\)]*\)//g) {
-## }
-## if ($ln =~ /,/) {
-## WARN("MULTIPLE_DECLARATION",
-## "declaring multiple variables together should be avoided\n" . $herecurr);
-## }
-## }
-
-#need space before brace following if, while, etc
- if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\){/) ||
- $line =~ /do\{/) {
- if (ERROR("SPACING",
- "space required before the open brace '{'\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/^(\+.*(?:do|\)))\{/$1 \{/;
- }
- }
-
-## # check for blank lines before declarations
-## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ &&
-## $prevrawline =~ /^.\s*$/) {
-## WARN("SPACING",
-## "No blank lines before declarations\n" . $hereprev);
-## }
-##
-
-# closing brace should have a space following it when it has anything
-# on the line
- if ($line =~ /}(?!(?:,|;|\)))\S/) {
- if (ERROR("SPACING",
- "space required after that close brace '}'\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/}((?!(?:,|;|\)))\S)/} $1/;
- }
- }
-
-# check spacing on square brackets
- if ($line =~ /\[\s/ && $line !~ /\[\s*$/) {
- if (ERROR("SPACING",
- "space prohibited after that open square bracket '['\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/\[\s+/\[/;
- }
- }
- if ($line =~ /\s\]/) {
- if (ERROR("SPACING",
- "space prohibited before that close square bracket ']'\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/\s+\]/\]/;
- }
- }
-
-# check spacing on parentheses
- if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ &&
- $line !~ /for\s*\(\s+;/) {
- if (ERROR("SPACING",
- "space prohibited after that open parenthesis '('\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/\(\s+/\(/;
- }
- }
- if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ &&
- $line !~ /for\s*\(.*;\s+\)/ &&
- $line !~ /:\s+\)/) {
- if (ERROR("SPACING",
- "space prohibited before that close parenthesis ')'\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/\s+\)/\)/;
- }
- }
-
-#goto labels aren't indented, allow a single space however
- if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and
- !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) {
- if (WARN("INDENTED_LABEL",
- "labels should not be indented\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/^(.)\s+/$1/;
- }
- }
-
-# return is not a function
- if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
- my $spacing = $1;
- if ($^V && $^V ge 5.10.0 &&
- $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) {
- my $value = $1;
- $value = deparenthesize($value);
- if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) {
- ERROR("RETURN_PARENTHESES",
- "return is not a function, parentheses are not required\n" . $herecurr);
- }
- } elsif ($spacing !~ /\s+/) {
- ERROR("SPACING",
- "space required before the open parenthesis '('\n" . $herecurr);
- }
- }
-
-# unnecessary return in a void function? (a single leading tab, then return;)
- if ($sline =~ /^\+\treturn\s*;\s*$/ &&
- $prevline =~ /^\+/) {
- WARN("RETURN_VOID",
- "void function return statements are not generally useful\n" . $herecurr);
- }
-
-# if statements using unnecessary parentheses - ie: if ((foo == bar))
- if ($^V && $^V ge 5.10.0 &&
- $line =~ /\bif\s*((?:\(\s*){2,})/) {
- my $openparens = $1;
- my $count = $openparens =~ tr@\(@\(@;
- my $msg = "";
- if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) {
- my $comp = $4; #Not $1 because of $LvalOrFunc
- $msg = " - maybe == should be = ?" if ($comp eq "==");
- WARN("UNNECESSARY_PARENTHESES",
- "Unnecessary parentheses$msg\n" . $herecurr);
- }
- }
-
-# Return of what appears to be an errno should normally be -'ve
- if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) {
- my $name = $1;
- if ($name ne 'EOF' && $name ne 'ERROR') {
- WARN("USE_NEGATIVE_ERRNO",
- "return of an errno should typically be -ve (return -$1)\n" . $herecurr);
- }
- }
-
-# Need a space before open parenthesis after if, while etc
- if ($line =~ /\b(if|while|for|switch)\(/) {
- if (ERROR("SPACING",
- "space required before the open parenthesis '('\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/\b(if|while|for|switch)\(/$1 \(/;
- }
- }
-
-# Check for illegal assignment in if conditional -- and check for trailing
-# statements after the conditional.
- if ($line =~ /do\s*(?!{)/) {
- ($stat, $cond, $line_nr_next, $remain_next, $off_next) =
- ctx_statement_block($linenr, $realcnt, 0)
- if (!defined $stat);
- my ($stat_next) = ctx_statement_block($line_nr_next,
- $remain_next, $off_next);
- $stat_next =~ s/\n./\n /g;
- ##print "stat<$stat> stat_next<$stat_next>\n";
-
- if ($stat_next =~ /^\s*while\b/) {
- # If the statement carries leading newlines,
- # then count those as offsets.
- my ($whitespace) =
- ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s);
- my $offset =
- statement_rawlines($whitespace) - 1;
-
- $suppress_whiletrailers{$line_nr_next +
- $offset} = 1;
- }
- }
- if (!defined $suppress_whiletrailers{$linenr} &&
- defined($stat) && defined($cond) &&
- $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
- my ($s, $c) = ($stat, $cond);
-
- if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
- ERROR("ASSIGN_IN_IF",
- "do not use assignment in if condition\n" . $herecurr);
- }
-
- # Find out what is on the end of the line after the
- # conditional.
- substr($s, 0, length($c), '');
- $s =~ s/\n.*//g;
- $s =~ s/$;//g; # Remove any comments
- if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
- $c !~ /}\s*while\s*/)
- {
- # Find out how long the conditional actually is.
- my @newlines = ($c =~ /\n/gs);
- my $cond_lines = 1 + $#newlines;
- my $stat_real = '';
-
- $stat_real = raw_line($linenr, $cond_lines)
- . "\n" if ($cond_lines);
- if (defined($stat_real) && $cond_lines > 1) {
- $stat_real = "[...]\n$stat_real";
- }
-
- ERROR("TRAILING_STATEMENTS",
- "trailing statements should be on next line\n" . $herecurr . $stat_real);
- }
- }
-
-# Check for bitwise tests written as boolean
- if ($line =~ /
- (?:
- (?:\[|\(|\&\&|\|\|)
- \s*0[xX][0-9]+\s*
- (?:\&\&|\|\|)
- |
- (?:\&\&|\|\|)
- \s*0[xX][0-9]+\s*
- (?:\&\&|\|\||\)|\])
- )/x)
- {
- WARN("HEXADECIMAL_BOOLEAN_TEST",
- "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr);
- }
-
-# if and else should not have general statements after it
- if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
- my $s = $1;
- $s =~ s/$;//g; # Remove any comments
- if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
- ERROR("TRAILING_STATEMENTS",
- "trailing statements should be on next line\n" . $herecurr);
- }
- }
-# if should not continue a brace
- if ($line =~ /}\s*if\b/) {
- ERROR("TRAILING_STATEMENTS",
- "trailing statements should be on next line\n" .
- $herecurr);
- }
-# case and default should not have general statements after them
- if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g &&
- $line !~ /\G(?:
- (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$|
- \s*return\s+
- )/xg)
- {
- ERROR("TRAILING_STATEMENTS",
- "trailing statements should be on next line\n" . $herecurr);
- }
-
- # Check for }<nl>else {, these must be at the same
- # indent level to be relevant to each other.
- if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and
- $previndent == $indent) {
- ERROR("ELSE_AFTER_BRACE",
- "else should follow close brace '}'\n" . $hereprev);
- }
-
- if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and
- $previndent == $indent) {
- my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0);
-
- # Find out what is on the end of the line after the
- # conditional.
- substr($s, 0, length($c), '');
- $s =~ s/\n.*//g;
-
- if ($s =~ /^\s*;/) {
- ERROR("WHILE_AFTER_BRACE",
- "while should follow close brace '}'\n" . $hereprev);
- }
- }
-
-#Specific variable tests
- while ($line =~ m{($Constant|$Lval)}g) {
- my $var = $1;
-
-#gcc binary extension
- if ($var =~ /^$Binary$/) {
- if (WARN("GCC_BINARY_CONSTANT",
- "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) &&
- $fix) {
- my $hexval = sprintf("0x%x", oct($var));
- $fixed[$linenr - 1] =~
- s/\b$var\b/$hexval/;
- }
- }
- }
-
-#no spaces allowed after \ in define
- if ($line =~ /\#\s*define.*\\\s+$/) {
- if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION",
- "Whitespace after \\ makes next lines useless\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/\s+$//;
- }
- }
-
-# multi-statement macros should be enclosed in a do while loop, grab the
-# first statement and ensure its the whole macro if its not enclosed
-# in a known good container
- if ($line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
- my $ln = $linenr;
- my $cnt = $realcnt;
- my ($off, $dstat, $dcond, $rest);
- my $ctx = '';
- ($dstat, $dcond, $ln, $cnt, $off) =
- ctx_statement_block($linenr, $realcnt, 0);
- $ctx = $dstat;
- #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n";
- #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
-
- $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//;
- $dstat =~ s/$;//g;
- $dstat =~ s/\\\n.//g;
- $dstat =~ s/^\s*//s;
- $dstat =~ s/\s*$//s;
-
- # Flatten any parentheses and braces
- while ($dstat =~ s/\([^\(\)]*\)/1/ ||
- $dstat =~ s/\{[^\{\}]*\}/1/ ||
- $dstat =~ s/\[[^\[\]]*\]/1/)
- {
- }
-
- # Flatten any obvious string concatentation.
- while ($dstat =~ s/("X*")\s*$Ident/$1/ ||
- $dstat =~ s/$Ident\s*("X*")/$1/)
- {
- }
-
- my $exceptions = qr{
- $Declare|
- __typeof__\(|
- union|
- struct|
- \.$Ident\s*=\s*|
- ^\"|\"$
- }x;
- #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
- if ($dstat ne '' &&
- $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(),
- $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo();
- $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz
- $dstat !~ /^'X'$/ && # character constants
- $dstat !~ /$exceptions/ &&
- $dstat !~ /^\.$Ident\s*=/ && # .foo =
- $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo
- $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...)
- $dstat !~ /^for\s*$Constant$/ && # for (...)
- $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar()
- $dstat !~ /^do\s*{/ && # do {...
- $dstat !~ /^\(\{/ && # ({...
- $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/)
- {
- $ctx =~ s/\n*$//;
- my $herectx = $here . "\n";
- my $cnt = statement_rawlines($ctx);
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
-
- if ($dstat =~ /;/) {
- ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
- "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
- } else {
- ERROR("COMPLEX_MACRO",
- "Macros with complex values should be enclosed in parenthesis\n" . "$herectx");
- }
- }
-
-# check for line continuations outside of #defines, preprocessor #, and asm
-
- } else {
- if ($prevline !~ /^..*\\$/ &&
- $line !~ /^\+\s*\#.*\\$/ && # preprocessor
- $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm
- $line =~ /^\+.*\\$/) {
- WARN("LINE_CONTINUATIONS",
- "Avoid unnecessary line continuations\n" . $herecurr);
- }
- }
-
-# do {} while (0) macro tests:
-# single-statement macros do not need to be enclosed in do while (0) loop,
-# macro should not end with a semicolon
- if ($^V && $^V ge 5.10.0 &&
- $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) {
- my $ln = $linenr;
- my $cnt = $realcnt;
- my ($off, $dstat, $dcond, $rest);
- my $ctx = '';
- ($dstat, $dcond, $ln, $cnt, $off) =
- ctx_statement_block($linenr, $realcnt, 0);
- $ctx = $dstat;
-
- $dstat =~ s/\\\n.//g;
-
- if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) {
- my $stmts = $2;
- my $semis = $3;
-
- $ctx =~ s/\n*$//;
- my $cnt = statement_rawlines($ctx);
- my $herectx = $here . "\n";
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
-
- if (($stmts =~ tr/;/;/) == 1 &&
- $stmts !~ /^\s*(if|while|for|switch)\b/) {
- WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
- "Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
- }
- if (defined $semis && $semis ne "") {
- WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON",
- "do {} while (0) macros should not be semicolon terminated\n" . "$herectx");
- }
- } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) {
- $ctx =~ s/\n*$//;
- my $cnt = statement_rawlines($ctx);
- my $herectx = $here . "\n";
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
-
- WARN("TRAILING_SEMICOLON",
- "macros should not use a trailing semicolon\n" . "$herectx");
- }
- }
-
-# check for redundant bracing round if etc
- if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) {
- my ($level, $endln, @chunks) =
- ctx_statement_full($linenr, $realcnt, 1);
- #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n";
- #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n";
- if ($#chunks > 0 && $level == 0) {
- my @allowed = ();
- my $allow = 0;
- my $seen = 0;
- my $herectx = $here . "\n";
- my $ln = $linenr - 1;
- for my $chunk (@chunks) {
- my ($cond, $block) = @{$chunk};
-
- # If the condition carries leading newlines, then count those as offsets.
- my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s);
- my $offset = statement_rawlines($whitespace) - 1;
-
- $allowed[$allow] = 0;
- #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n";
-
- # We have looked at and allowed this specific line.
- $suppress_ifbraces{$ln + $offset} = 1;
-
- $herectx .= "$rawlines[$ln + $offset]\n[...]\n";
- $ln += statement_rawlines($block) - 1;
-
- substr($block, 0, length($cond), '');
-
- $seen++ if ($block =~ /^\s*{/);
-
- #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n";
- if (statement_lines($cond) > 1) {
- #print "APW: ALLOWED: cond<$cond>\n";
- $allowed[$allow] = 1;
- }
- if ($block =~/\b(?:if|for|while)\b/) {
- #print "APW: ALLOWED: block<$block>\n";
- $allowed[$allow] = 1;
- }
- if (statement_block_size($block) > 1) {
- #print "APW: ALLOWED: lines block<$block>\n";
- $allowed[$allow] = 1;
- }
- $allow++;
- }
- if ($seen) {
- my $sum_allowed = 0;
- foreach (@allowed) {
- $sum_allowed += $_;
- }
- if ($sum_allowed == 0) {
- WARN("BRACES",
- "braces {} are not necessary for any arm of this statement\n" . $herectx);
- } elsif ($sum_allowed != $allow &&
- $seen != $allow) {
- CHK("BRACES",
- "braces {} should be used on all arms of this statement\n" . $herectx);
- }
- }
- }
- }
- if (!defined $suppress_ifbraces{$linenr - 1} &&
- $line =~ /\b(if|while|for|else)\b/) {
- my $allowed = 0;
-
- # Check the pre-context.
- if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) {
- #print "APW: ALLOWED: pre<$1>\n";
- $allowed = 1;
- }
-
- my ($level, $endln, @chunks) =
- ctx_statement_full($linenr, $realcnt, $-[0]);
-
- # Check the condition.
- my ($cond, $block) = @{$chunks[0]};
- #print "CHECKING<$linenr> cond<$cond> block<$block>\n";
- if (defined $cond) {
- substr($block, 0, length($cond), '');
- }
- if (statement_lines($cond) > 1) {
- #print "APW: ALLOWED: cond<$cond>\n";
- $allowed = 1;
- }
- if ($block =~/\b(?:if|for|while)\b/) {
- #print "APW: ALLOWED: block<$block>\n";
- $allowed = 1;
- }
- if (statement_block_size($block) > 1) {
- #print "APW: ALLOWED: lines block<$block>\n";
- $allowed = 1;
- }
- # Check the post-context.
- if (defined $chunks[1]) {
- my ($cond, $block) = @{$chunks[1]};
- if (defined $cond) {
- substr($block, 0, length($cond), '');
- }
- if ($block =~ /^\s*\{/) {
- #print "APW: ALLOWED: chunk-1 block<$block>\n";
- $allowed = 1;
- }
- }
- if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) {
- my $herectx = $here . "\n";
- my $cnt = statement_rawlines($block);
-
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
-
- WARN("BRACES",
- "braces {} are not necessary for single statement blocks\n" . $herectx);
- }
- }
-
-# check for unnecessary blank lines around braces
- if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) {
- CHK("BRACES",
- "Blank lines aren't necessary before a close brace '}'\n" . $hereprev);
- }
- if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) {
- CHK("BRACES",
- "Blank lines aren't necessary after an open brace '{'\n" . $hereprev);
- }
-
-# warn about #if 0
- if ($line =~ /^.\s*\#\s*if\s+0\b/) {
- CHK("REDUNDANT_CODE",
- "if this code is redundant consider removing it\n" .
- $herecurr);
- }
-
-# check for needless "if (<foo>) fn(<foo>)" uses
- if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) {
- my $expr = '\s*\(\s*' . quotemeta($1) . '\s*\)\s*;';
- if ($line =~ /\b(free)$expr/) {
- WARN('NEEDLESS_IF',
- "$1(NULL) is safe this check is probably not required\n" . $hereprev);
- }
- }
-
-# warn about #ifdefs in C files
-# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) {
-# print "#ifdef in C files should be avoided\n";
-# print "$herecurr";
-# $clean = 0;
-# }
-
-# warn about spacing in #ifdefs
- if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) {
- if (ERROR("SPACING",
- "exactly one space required after that #$1\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~
- s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /;
- }
-
- }
-
-# Check that the storage class is at the beginning of a declaration
- if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) {
- WARN("STORAGE_CLASS",
- "storage class should be at the beginning of the declaration\n" . $herecurr)
- }
-
-# check the location of the inline attribute, that it is between
-# storage class and type.
- if ($line =~ /\b$Type\s+$Inline\b/ ||
- $line =~ /\b$Inline\s+$Storage\b/) {
- ERROR("INLINE_LOCATION",
- "inline keyword should sit between storage class and type\n" . $herecurr);
- }
-
-# Check for __inline__ and __inline, prefer inline
- if ($line =~ /\b(__inline__|__inline)\b/) {
- if (WARN("INLINE",
- "plain inline is preferred over $1\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/\b(__inline__|__inline)\b/inline/;
-
- }
- }
-
-# check for sizeof(&)
- if ($line =~ /\bsizeof\s*\(\s*\&/) {
- WARN("SIZEOF_ADDRESS",
- "sizeof(& should be avoided\n" . $herecurr);
- }
-
-# check for sizeof without parenthesis
- if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) {
- if (WARN("SIZEOF_PARENTHESIS",
- "sizeof $1 should be sizeof($1)\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex;
- }
- }
-
-# check for line continuations in quoted strings with odd counts of "
- if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) {
- WARN("LINE_CONTINUATIONS",
- "Avoid line continuations in quoted strings\n" . $herecurr);
- }
-
-# Check for misused memsets
- if ($^V && $^V ge 5.10.0 &&
- defined $stat &&
- $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/s) {
-
- my $ms_addr = $2;
- my $ms_val = $7;
- my $ms_size = $12;
-
- if ($ms_size =~ /^(0x|)0$/i) {
- ERROR("MEMSET",
- "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n");
- } elsif ($ms_size =~ /^(0x|)1$/i) {
- WARN("MEMSET",
- "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n");
- }
- }
-
-# check for naked sscanf
- if ($^V && $^V ge 5.10.0 &&
- defined $stat &&
- $line =~ /\bsscanf\b/ &&
- ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ &&
- $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ &&
- $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) {
- my $lc = $stat =~ tr@\n@@;
- $lc = $lc + $linenr;
- my $stat_real = raw_line($linenr, 0);
- for (my $count = $linenr + 1; $count <= $lc; $count++) {
- $stat_real = $stat_real . "\n" . raw_line($count, 0);
- }
- WARN("NAKED_SSCANF",
- "unchecked sscanf return value\n" . "$here\n$stat_real\n");
- }
-
-# check for new externs in .h files.
- if ($realfile =~ /\.h$/ &&
- $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) {
- if (CHK("AVOID_EXTERNS",
- "extern prototypes should be avoided in .h files\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/(.*)\bextern\b\s*(.*)/$1$2/;
- }
- }
-
-# check for new externs in .c files.
- if ($realfile =~ /\.c$/ && defined $stat &&
- $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
- {
- my $function_name = $1;
- my $paren_space = $2;
-
- my $s = $stat;
- if (defined $cond) {
- substr($s, 0, length($cond), '');
- }
- if ($s =~ /^\s*;/ &&
- $function_name ne 'uninitialized_var')
- {
- WARN("AVOID_EXTERNS",
- "externs should be avoided in .c files\n" . $herecurr);
- }
-
- if ($paren_space =~ /\n/) {
- WARN("FUNCTION_ARGUMENTS",
- "arguments for function declarations should follow identifier\n" . $herecurr);
- }
-
- } elsif ($realfile =~ /\.c$/ && defined $stat &&
- $stat =~ /^.\s*extern\s+/)
- {
- WARN("AVOID_EXTERNS",
- "externs should be avoided in .c files\n" . $herecurr);
- }
-
-# check for multiple semicolons
- if ($line =~ /;\s*;\s*$/) {
- if (WARN("ONE_SEMICOLON",
- "Statements terminations use 1 semicolon\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/(\s*;\s*){2,}$/;/g;
- }
- }
-
-# check for case / default statements not preceeded by break/fallthrough/switch
- if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
- my $has_break = 0;
- my $has_statement = 0;
- my $count = 0;
- my $prevline = $linenr;
- while ($prevline > 1 && $count < 3 && !$has_break) {
- $prevline--;
- my $rline = $rawlines[$prevline - 1];
- my $fline = $lines[$prevline - 1];
- last if ($fline =~ /^\@\@/);
- next if ($fline =~ /^\-/);
- next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/);
- $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i);
- next if ($fline =~ /^.[\s$;]*$/);
- $has_statement = 1;
- $count++;
- $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/);
- }
- if (!$has_break && $has_statement) {
- WARN("MISSING_BREAK",
- "Possible switch case/default not preceeded by break or fallthrough comment\n" . $herecurr);
- }
- }
-
-# check for switch/default statements without a break;
- if ($^V && $^V ge 5.10.0 &&
- defined $stat &&
- $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) {
- my $ctx = '';
- my $herectx = $here . "\n";
- my $cnt = statement_rawlines($stat);
- for (my $n = 0; $n < $cnt; $n++) {
- $herectx .= raw_line($linenr, $n) . "\n";
- }
- WARN("DEFAULT_NO_BREAK",
- "switch default: should use break\n" . $herectx);
- }
-
-# check for gcc specific __FUNCTION__
- if ($line =~ /\b__FUNCTION__\b/) {
- if (WARN("USE_FUNC",
- "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) &&
- $fix) {
- $fixed[$linenr - 1] =~ s/\b__FUNCTION__\b/__func__/g;
- }
- }
-
-# check for comparisons against true and false
- if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) {
- my $lead = $1;
- my $arg = $2;
- my $test = $3;
- my $otype = $4;
- my $trail = $5;
- my $op = "!";
-
- ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i);
-
- my $type = lc($otype);
- if ($type =~ /^(?:true|false)$/) {
- if (("$test" eq "==" && "$type" eq "true") ||
- ("$test" eq "!=" && "$type" eq "false")) {
- $op = "";
- }
-
- CHK("BOOL_COMPARISON",
- "Using comparison to $otype is error prone\n" . $herecurr);
-
-## maybe suggesting a correct construct would better
-## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr);
-
- }
- }
-
-# check for %L{u,d,i} in strings
- my $string;
- while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
- $string = substr($rawline, $-[1], $+[1] - $-[1]);
- $string =~ s/%%/__/g;
- if ($string =~ /(?<!%)%L[udi]/) {
- WARN("PRINTF_L",
- "\%Ld/%Lu are not-standard C, use %lld/%llu\n" . $herecurr);
- last;
- }
- }
- }
-
- # If we have no input at all, then there is nothing to report on
- # so just keep quiet.
- if ($#rawlines == -1) {
- exit(0);
- }
-
- # In mailback mode only produce a report in the negative, for
- # things that appear to be patches.
- if ($mailback && ($clean == 1 || !$is_patch)) {
- exit(0);
- }
-
- # This is not a patch, and we are are in 'no-patch' mode so
- # just keep quiet.
- if (!$chk_patch && !$is_patch) {
- exit(0);
- }
-
- if (!$is_patch) {
- ERROR("NOT_UNIFIED_DIFF",
- "Does not appear to be a unified-diff format patch\n");
- }
- if ($is_patch && $chk_signoff && $signoff == 0) {
- ERROR("MISSING_SIGN_OFF",
- "Missing Signed-off-by: line(s)\n");
- }
-
- print report_dump();
- if ($summary && !($clean == 1 && $quiet == 1)) {
- print "$filename " if ($summary_file);
- print "total: $cnt_error errors, $cnt_warn warnings, " .
- (($check)? "$cnt_chk checks, " : "") .
- "$cnt_lines lines checked\n";
- print "\n" if ($quiet == 0);
- }
-
- if ($quiet == 0) {
-
- if ($^V lt 5.10.0) {
- print("NOTE: perl $^V is not modern enough to detect all possible issues.\n");
- print("An upgrade to at least perl v5.10.0 is suggested.\n\n");
- }
-
- }
-
- hash_show_words(\%use_type, "Used");
- hash_show_words(\%ignore_type, "Ignored");
-
- if ($clean == 0 && $fix && "@rawlines" ne "@fixed") {
- my $newfile = $filename;
- $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace);
- my $linecount = 0;
- my $f;
-
- open($f, '>', $newfile)
- or die "$P: Can't open $newfile for write\n";
- foreach my $fixed_line (@fixed) {
- $linecount++;
- if ($file) {
- if ($linecount > 3) {
- $fixed_line =~ s/^\+//;
- print $f $fixed_line. "\n";
- }
- } else {
- print $f $fixed_line . "\n";
- }
- }
- close($f);
-
- if (!$quiet) {
- print << "EOM";
-Wrote EXPERIMENTAL --fix correction(s) to '$newfile'
-
-Do _NOT_ trust the results written to this file.
-Do _NOT_ submit these changes without inspecting them for correctness.
-
-This EXPERIMENTAL file is simply a convenience to help rewrite patches.
-No warranties, expressed or implied...
-
-EOM
- }
- }
-
- if ($clean == 1 && $quiet == 0) {
- print "$vname has no obvious style problems and is ready for submission.\n"
- }
- if ($clean == 0 && $quiet == 0) {
- print << "EOM";
-$vname has style problems, please review.
-EOM
- }
-
- return $clean;
-}
diff --git a/scripts/test-branch.py b/scripts/test-branch.py
deleted file mode 100755
index c7dae60..0000000
--- a/scripts/test-branch.py
+++ /dev/null
@@ -1,84 +0,0 @@
-#! /usr/bin/env python3
-#
-# test-branch.py: 'make check' on each commit on a branch
-#
-# Copyright (C) 2013 Paul Barker
-#
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# 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, 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.
-
-import subprocess as sp
-import os, shutil
-
-def fail(msg):
- print(msg)
- exit(-1)
-
-def rsh_s(cmd):
- data = sp.check_output(cmd, shell=True)
- s = data.decode("utf-8")
- return s
-
-def sh(cmd):
- return sp.call(cmd, shell=True)
-
-def rsh(cmd):
- return sp.check_call(cmd, shell=True)
-
-def read_env(key, default_value):
- value = os.environ.get(key)
- if value:
- return value
- else:
- return default_value
-
-def list_commits(c_from, c_to):
- s = rsh_s("git rev-list %s..%s" % (c_from, c_to))
- commits = s.strip().split("\n")
- commits.reverse()
- return commits
-
-def do_checkout(commit):
- os.chdir(opkg_dir)
- rsh("git checkout %s" % commit)
-
-def do_test():
- os.chdir(opkg_dir)
- rsh("./autogen.sh")
- rsh("./configure %s" % OPKG_CONFIGURE_OPTIONS)
- rsh("make %s" % OPKG_MAKE_OPTIONS)
- rsh("make check")
- rsh("make DESTDIR=%s install" % install_dir)
- print("\n")
-
-OPKG_MASTER = read_env("OPKG_MASTER", "master")
-OPKG_BRANCH = read_env("OPKG_BRANCH", "HEAD")
-OPKG_TEST_DIR = read_env("OPKG_TEST_DIR", "/tmp/opkg-test")
-OPKG_CONFIGURE_OPTIONS = read_env("OPKG_CONFIGURE_OPTIONS", "--enable-sha256")
-OPKG_MAKE_OPTIONS = read_env("OPKG_MAKE_OPTIONS", "")
-
-repo_dir = os.getcwd()
-commits = list_commits(OPKG_MASTER, OPKG_BRANCH)
-
-for c in commits:
- print("========== %s ==========\n" % (c))
-
- commit_dir = os.path.join(OPKG_TEST_DIR, c)
- opkg_dir = os.path.join(commit_dir, "opkg")
- install_dir = os.path.join(commit_dir, "install")
-
- os.makedirs(opkg_dir, exist_ok=True)
- os.makedirs(install_dir, exist_ok=True)
-
- rsh("git clone %s %s" % (repo_dir, opkg_dir))
- do_checkout(c)
- do_test()
--
2.38.1

Alex Stewart

unread,
Jan 20, 2023, 2:33:55 PM1/20/23
to opkg-...@googlegroups.com
Pulled this patchset into master, starting with commit
122c8a03bc66b098fb8bd62c0d48a9be197d1368 [1].

[1]
https://git.yoctoproject.org/opkg/commit/?id=122c8a03bc66b098fb8bd62c0d48a9be197d1368
Alex Stewart
Software Engineer - NI Real-Time OS
NI (National Instruments)

alex.s...@ni.com

Reply all
Reply to author
Forward
0 new messages