Page MenuHomeFreeBSD

www/nginx-module-njs: new port, dynamic module of nginx-module-njs
AbandonedPublic

Authored by ronald_klop.ws on Sun, Aug 23, 11:22 AM.
Tags
None
Referenced Files
F170827081: D59121.diff
Sun, Sep 6, 9:15 PM
F170817089: D59121.diff
Sun, Sep 6, 7:54 PM
F170765305: D59121.diff
Sun, Sep 6, 12:23 PM
Unknown Object (File)
Sat, Sep 5, 5:08 PM
Unknown Object (File)
Sat, Sep 5, 5:07 PM
Unknown Object (File)
Sat, Sep 5, 1:16 PM
Unknown Object (File)
Sat, Sep 5, 12:17 PM
Unknown Object (File)
Sat, Sep 5, 11:40 AM
Subscribers
None

Details

Summary

Makes it possible to more granular install nginx with modules
without needing to install nginx-full.

A MASTER port structure is used so the distinfo is kept in sync.

Versioning is similar to what is used for Debian packages upstream.
See: https://nginx.org/packages/debian/pool/nginx/n/nginx-module-njs/
<nginx_version>+<njs_version> -> nginx-module-njs-1.30.4+1.0.0.pkg
This makes sure the package is rebuild if the version of the master port
or the NJS version changes.

NB: with some changes to the MASTER port it should be possible to reuse
more infrastructure, but I left that as future improvements.

BTW: I set myself as maintainer, but I'm fine if you want to keep this port under your nginx maintenance.

Test Plan

I installed the created module and ran it on my server.

Diff Detail

Repository
R11 FreeBSD ports repository
Lint
No Lint Coverage
Unit
No Test Coverage
Build Status
Buildable 76178
Build 73061: arc lint + arc unit

Event Timeline

ronald_klop.ws created this revision.

Hi Ronald,

thanks for doing this. You are ahead of me here: I have been carrying the same
idea around for a while (splitting the 3rd party modules out of www/nginx), but
I am still busy with the Lua ports, so seeing njs land first is a nice surprise.

One thing upfront. With 1.0.0 the built-in njs engine is deprecated:

The built-in njs JavaScript engine is deprecated since 1.0.0; new
configurations should use the QuickJS engine.
https://nginx.org/en/docs/njs/
https://github.com/nginx/njs/releases/tag/1.0.0

It only gets bug fixes from here on, everything new goes into QuickJS. A port we
add in 2026 should use QuickJS from day one, and right now this one does the
opposite (see 1.).

About maintainership: I would like to take the port over, it makes things easier
for me alongside www/nginx, www/nginx-devel and www/freenginx, where the same
module versions show up. If you would rather keep it, that is completely fine
with me and I am happy to just review.

I ran poudriere testport on 151amd64. It builds fine, but a few things came out:

  1. QuickJS never gets enabled and nothing says so. njs autodetects it and does not fail when it is missing (nginx/config: NJS_QUICKJS=${NJS_QUICKJS:-YES}). All five probes fail in the jail:

    checking for QuickJS library -lquickjs ... not found checking for QuickJS-NG library -lqjs ... not found

    There is no dependency on lang/quickjs, and the include path would be wrong anyway: the header is at include/quickjs/quickjs.h while the port only passes -I ${LOCALBASE}/include. Compare lang/njs, which uses --cc-opt="-I${LOCALBASE}/include/quickjs" --with-quickjs. Note it also cuts the other way: with lang/quickjs-ng installed the -lqjs probe succeeds, so the result depends on the build host. www/nginx pins the off case with NJS_QUICKJS=NO for that reason.
  1. The new pkg-descr is never read. With MASTERDIR set, DESCR comes from the master port, so the package ships the nginx description:

    $ make -V DESCR .../www/nginx-module-njs/../nginx/pkg-descr

    Same for distinfo, FILESDIR and PATCHDIR. You already worked around this for PLIST.
  1. Only the HTTP module is built, --with-stream is never passed, so ngx_stream_js_module.so is missing. In www/nginx, NJS implies STREAM and we ship both.
  1. www/nginx with NJS enabled installs the exact same file (libexec/nginx/ngx_http_js_module.so), so CONFLICTS_INSTALL is needed.
  1. No RUN_DEPENDS on nginx and no version bound. --with-compat relaxes the module signature, but nothing stops pkg from installing the module next to a different nginx or next to nginx-lite.
  1. portlint gives 66 fatal errors and 59 warnings, all from inheriting nginx' OPTIONS_DEFINE through Makefile.extmod while the *_DESC lines live in www/nginx/Makefile.
  1. Minor: PLIST points at a pkg-plist that is not in the diff (works only because PLIST_FILES is set), USES=cpe without CPE_VENDOR gets the CPE wrong (lang/njs uses f5), and libxml2/libxslt are unconditional here while they are the NJS_XML sub-option in www/nginx.

Last one is a question rather than a defect: www/nginx-module-lua already exists
and means something else, it is NO_BUILD and only installs the module sources
for www/nginx to build against. If nginx-module-* is going to mean "prebuilt
DSO" from now on, I would like us to agree on that first, since I want to split
out the remaining modules the same way.

The plus sign in PORTVERSION works fine by the way, pkg handled
nginx-module-njs-1.30.4+1.0.0 without complaining.

Thanks again for getting this started.

Best regards,
Jochen

Address the concerns mentioned in the first review.

Fixed maintainer to joneum.
Fixed 1. Quick JS
Fixed 2. DESCR, FILESDIR, PATCHDIR
Fixed 3. STREAM
Todo 4. CONFLICTS_INSTALL, we can't add conflict on nginx (as is it is RUN_DEPENDS) and don't know if option NJS is set. Could add a conflict with nginx-full as that includes NJS, but nginx itself already conflicts with nginx-full.
Fixed 5. RUN_DEPENDS
Fixed 6. portlint errors OPTIONS_DEFINE vs. *_DESC.
Fixed 7. CPE_VENDOR
Todo Naming, I prefer to follow the naming of the upstream DSO modules: nginx-module-XYZ.

Hi Ronald,

thanks for doing this. You are ahead of me here: I have been carrying the same
idea around for a while (splitting the 3rd party modules out of www/nginx), but
I am still busy with the Lua ports, so seeing njs land first is a nice surprise.

Nice to hear that you are positive about this.

One thing upfront. With 1.0.0 the built-in njs engine is deprecated:

The built-in njs JavaScript engine is deprecated since 1.0.0; new
configurations should use the QuickJS engine.
https://nginx.org/en/docs/njs/
https://github.com/nginx/njs/releases/tag/1.0.0

It only gets bug fixes from here on, everything new goes into QuickJS. A port we
add in 2026 should use QuickJS from day one, and right now this one does the
opposite (see 1.).

About maintainership: I would like to take the port over, it makes things easier
for me alongside www/nginx, www/nginx-devel and www/freenginx, where the same
module versions show up. If you would rather keep it, that is completely fine
with me and I am happy to just review.

I have set the maintainer to you.

I ran poudriere testport on 151amd64. It builds fine, but a few things came out:

  1. QuickJS never gets enabled and nothing says so. njs autodetects it and does not fail when it is missing (nginx/config: NJS_QUICKJS=${NJS_QUICKJS:-YES}). All five probes fail in the jail:

    checking for QuickJS library -lquickjs ... not found checking for QuickJS-NG library -lqjs ... not found

    There is no dependency on lang/quickjs, and the include path would be wrong anyway: the header is at include/quickjs/quickjs.h while the port only passes -I ${LOCALBASE}/include. Compare lang/njs, which uses --cc-opt="-I${LOCALBASE}/include/quickjs" --with-quickjs. Note it also cuts the other way: with lang/quickjs-ng installed the -lqjs probe succeeds, so the result depends on the build host. www/nginx pins the off case with NJS_QUICKJS=NO for that reason.

I added quickjs. It works on my server now.
NB: --with-quickjs gives me an configure error. The option does not exist. But it works without this flag.

  1. The new pkg-descr is never read. With MASTERDIR set, DESCR comes from the master port, so the package ships the nginx description:

    $ make -V DESCR .../www/nginx-module-njs/../nginx/pkg-descr

    Same for distinfo, FILESDIR and PATCHDIR. You already worked around this for PLIST.

These are fixed by setting them explicitly.
But, the distinfo is shared on purpose. Because the sources of both ports need to be in sync. If the MASTER port changes, the "slave" ports change in sync. The makesum target is key to how this MASTER port construction works.

  1. Only the HTTP module is built, --with-stream is never passed, so ngx_stream_js_module.so is missing. In www/nginx, NJS implies STREAM and we ship both.

fixed

  1. www/nginx with NJS enabled installs the exact same file (libexec/nginx/ngx_http_js_module.so), so CONFLICTS_INSTALL is needed.

I'm not sure how to do this without breaking the next point about RUN_DEPENDS. The pkg tool is also smart enough to see that another port already installed a file and will complain AFAIK.
I would propose to leave this for now. Unless you have a concrete construction for me to add to my port.

  1. No RUN_DEPENDS on nginx and no version bound. --with-compat relaxes the module signature, but nothing stops pkg from installing the module next to a different nginx or next to nginx-lite.

fixed

  1. portlint gives 66 fatal errors and 59 warnings, all from inheriting nginx' OPTIONS_DEFINE through Makefile.extmod while the *_DESC lines live in www/nginx/Makefile.

I fixed this by overriding a the OPTIONS_GROUP from Makefile.extmod. I think this is fine for a first version. Some ideas below.

  1. Minor: PLIST points at a pkg-plist that is not in the diff (works only because PLIST_FILES is set), USES=cpe without CPE_VENDOR gets the CPE wrong (lang/njs uses f5), and libxml2/libxslt are unconditional here while they are the NJS_XML sub-option in www/nginx.

I have libxml2/libxslt still unconditional. I don't know if this is a really big concern. I can make it optional in the module. For me the purpose of having a module pkg is to install the default and not fiddle with options for ports.

Last one is a question rather than a defect: www/nginx-module-lua already exists
and means something else, it is NO_BUILD and only installs the module sources
for www/nginx to build against. If nginx-module-* is going to mean "prebuilt
DSO" from now on, I would like us to agree on that first, since I want to split
out the remaining modules the same way.

My choice was to follow the naming of upstream. I think that is user friendly because it follows what users expect from other platforms like Debian.

Nice trick with letting the port copy the source as if it is the content of the package and then letting nginx copy it back under its own source tree. :-)
I would propose:

  1. Let the nginx-module-lua install a DSO similar nginx-module-njs.
  2. or let the nginx port extract the source file openresty-lua-nginx-module-v0.10.29_GH0.tar.gz.

But I'm just talking out loud here. I didn't try this.

The plus sign in PORTVERSION works fine by the way, pkg handled
nginx-module-njs-1.30.4+1.0.0 without complaining.

Super

Thanks again for getting this started.

My pleasure.
The current construction works and I think it is even good enough to be committed, IMHO. I'm happy to hear your thoughts.

But outside of this review I think it would be interesting to discuss how we can make a DSO pkg more easily.
Currently the slave port can't include the MASTERDIR/Makefile because that imports a lot of unused or even conflicting settings. At the same time my port now repeated some stuff which is also in the nginx/Makefile*. It would be nice to re-use some of the settings.
A question arises... do we want a hybrid solution with an NJS option in the nginx port + a separate njs port or is a separate njs package the way to go and should we drop the NJS option of the nginx port somewhere in the future?

Anyway, just thinking out load again and writing down some of my findings in the process. This goes beyond the review of the current patch.

Best regards,
Jochen

Regards,
Ronald.

Re-use some more of the infra of the master port by using OPTIONS_SLAVE=.

This imports some of the variables from Makefile.extmod.

Hi Ronald,

first of all, sorry. You put real work into this, you worked through eight
review points within a day and sent two updated diffs, and now I am the one
changing the direction. That is on me, I should have said this before you
reworked the port, not after.

Here is the problem. A prebuilt DSO package for an nginx module is a
construction I already had in the tree and had to take back out a week ago. In
ef8818183ced (2026-08-19) I converted www/nginx-module-lua from a DSO port into
a plain source-only port. The commit message gives the reason: the prebuilt .so
conflicted with the module the nginx ports build themselves. For the record,
bug 297651 that the commit references is about something else, namely the
COPYTREE_SHARE chmod problem in the pre-configure targets. The DSO removal rode
along in the same commit.

The conflict itself you can verify without taking my word for it. With NJS
enabled, www/nginx installs exactly the paths your package would write as well:

www/nginx/pkg-plist:55  %%DSO%%%%HTTP%%%%NJS%%libexec/nginx/ngx_http_js_module.so
www/nginx/pkg-plist:77  %%DSO%%%%STREAM%%%%NJS%%libexec/nginx/ngx_stream_js_module.so

And behind that sits the real problem: pkg cannot express that a module DSO
belongs to one specific server build. --with-compat relaxes the signature
check, but it does not make a module built against 1.30.4 safe to load into
whatever nginx a user happens to have installed.

So I would like to do njs the same way as lua: a plain source port that ships
the module sources, with the module still being built by the nginx ports. That
is also the scheme I want for the remaining 61 modules that are still GH_TUPLE
entries in Makefile.extmod, and I would rather not have nginx-module-* mean two
different things in the tree.

That is why I did the rework myself. You reworked the port twice and did very
clean work on it, and I do not want you to do it a third time just because I am
changing direction. The rework is finished and tested, there is nothing left
for you to do on it. I would be glad if you had a look at it though:

http://joneum.joneumbox.org/nginx-module-njs-srcport.diff

What it contains:

  • www/nginx-module-njs becomes NO_BUILD/NO_ARCH and installs the njs sources to share/nginx/modules/nginx-module-njs
  • all three server ports replace NJS_GH_TUPLE with NJS_BUILD_DEPENDS= nginx-module-njs>0:www/nginx-module-njs, plus WRKSRC_njs and a pre-configure-NJS-on that copies the sources into WRKDIR, exactly like LUA does
  • the njs lines drop out of the three distinfo files

Tested on 151amd64 unless noted otherwise:

  • testport www/nginx-module-njs, additionally on 144amd64
  • testport www/nginx with NJS NJS_QJS NJS_XML, www/nginx-devel with NJS, www/freenginx with NJS NJS_QJS NJS_XML, all of them with "enabled QuickJS engine"
  • testport www/nginx with NJS but without NJS_QJS and NJS_XML, to show that NJS_QUICKJS=NO keeps the engine choice deterministic
  • testport www/nginx without NJS at all, to show that the distinfo change does not break the default build
  • source port and www/nginx once more under BUILD_AS_NON_ROOT=yes with USE_TMPFS=no, since that is the configuration 297651 showed up in
  • runtime on a 15.1-RELEASE VM: load_module plus js_import and js_content answer with "njs works, engine=QuickJS, version=1.0.0", and js_preread works in the stream context as well

Two things I am not happy with, I would like your opinion on them. This is
meant as a question, not as an assignment:

  1. The port has to ship ts/ and test/ts/, because njs' auto/sources runs "find ts/" and "find test/ts/" unconditionally and configure dies otherwise. Shipping test data feels wrong to me. The alternative would be a patch against auto/sources, which means diverging from upstream. What would you do?
  1. I only did the runtime test for nginx, not for nginx-devel and freenginx, and only on amd64.

One more note in case you reproduce this by hand: my nginx is built with
--with-stream=dynamic, so ngx_stream_module.so has to be loaded before
ngx_stream_js_module.so, otherwise you get "Undefined symbol
ngx_stream_module".

Best regards,
Jochen

Oh, that is a pity.

We might have different goals with the nginx-module-XYZ ports.

I would love to be able to install DSO modules as binary packages so I don't need to recompile nginx with different options locally. This is how I can install modules in Debian.

I have the feeling that you have a different purpose with the module packages. If I guess correctly, I think you want to use the packages as some kind of vessel to be able to import 3rd party source code into the build of nginx. Something like "source-packages". I hope that will not confuse the end-users who expect binary packages.

About your question regarding "test/ts/". I have no idea. I didn't look into these directories.

Is my guess about "source-packages" true? Understanding what you are doing might help me form ideas.

Hi Ronald,

you guessed right. The nginx-module-* ports in the nginx family are source packages. They carry the module sources, and the module itself is built by the nginx ports. There is no prebuilt module inside them.

The reason really comes down to one thing, and my whole scheme hangs off it:

If a module is an option in the server port, it becomes a source port. If it is not an option, it can be a binary port.

www/nginx, www/nginx-devel and www/freenginx offer njs as the NJS option. Anyone who sets it already has ngx_http_js_module.so and ngx_stream_js_module.so in the server package, see www/nginx/pkg-plist lines 55 and 77. A module package writing the same paths gets in the way of that. That is why www/nginx-module-lua became a source port back in August.

So your model is not wrong, it just does not fit here. There is a nice counter-example in the same tree: www/angie-module-njs does exactly what you want, and there are 27 of those, neatly tied to the server version with RUN_DEPENDS= angie==1.12.1:www/angie. The difference is not the mechanism, it is the server. www/angie has no module options at all and never ships a module itself, so nothing can overlap there. If installing modules as binary packages matters to you, that is where you already have exactly the model you describe today, njs included.

You ask whether the NJS option should eventually be dropped from the nginx port. No. It has been there for years, a lot of people build their nginx exactly like that, and it is going to stay. That settles njs as a source port.

Your point about end users is fair, I did think about it. Within the nginx family the meaning is at least consistent, every one of those modules is an option there, so all nginx-module-* ports are source ports. The pkg-descr says so explicitly, and the files land in share/nginx/modules/ rather than in libexec/nginx/. The one that does not fit the picture yet is security/modsecurity3-nginx, I will clean that up separately from njs.

About ts/ and test/ts/: no worries, I will leave it as it is for now.

The patch is still here, unchanged:

http://joneum.joneumbox.org/nginx-module-njs-srcport.diff

How this goes on: I will wait and see whether you spot anything. If nothing comes up, I will commit the source port in the next few days and close D59121 afterwards, since the route is a different one now. If you do notice something about the patch, please say so and we will fold it in first. security/modsecurity3-nginx follows in a separate step.

And thanks again. You put a lot of hours into this, you responded to every single point and improved the port twice. I am genuinely sorry that a different route comes out of it in the end, your work deserved better than that. It did help though: without you pushing back I would not have looked at the whole scheme this closely.

Best regards, Jochen

Oh wow. I didn't notice angie in the ports tree before.
The angie port has indeed the structure with binary modules that I wished the nginx port had. The need to recompile nginx for every update always breaks my server as it interferes with the regular "pkg upgrade" I do.

I prefer to run the original nginx as I trust it more on security updates, the number of people having a look at the source code and 3rd part support.

...
Not to sound negative, but before pressing Submit I decided to first take a look and I just did this command and adjusted some configuration.
$ pkg install angie angie-module-njs

This service is now setup properly without any port options or recompiling. To me, as an end user, this is a really friendly way of installing stuff.
Thinking about it.. please consider what happens if somebody does "pkg install nginx nginx-module-njs" when nginx-module-njs is a source package.

Anyway. I respect your choices.
As I see it now, I can only ask you to reconsider the structure of nginx and hope the port will move from port options to binary module packages some day.