Trying to build both apk and ipk

Hi all

So I'm trying to modify the build system in order to make it build both apk and ipk packages for my project (because I like to include OpenWrt packages in the Github releases).

Looks like the only "normal" way to do this is to set/unset CONFIG_USE_APK=y in .config. But this way I have to run the compile command twice (once to build apk and another time to build ipk).

So I was thinking that there should be a way to achieve this by modifying the .mk files.

Currently I have this script:

https://pastebin.com/Eve089Ay

(yes, I know that this is crude and won't work after package-pack.mk is changed upstream, but good enough for now)

The script creates files include/package-pack-apk.mk and include/package-pack-ipk.mk (which I generate from package-pack.mk by removing the checks for CONFIG_USE_APK and the following code for either apk or ipk):

https://pastebin.com/qdCWKwxv
https://pastebin.com/kJQyCNyB

The script also modifies include/package.mk to point to these files (rather than to include/package-pack.mk). I also modified include/default-packages.mk this way:

#ifneq ($(CONFIG_USE_APK),)
  DEFAULT_PACKAGES += apk-mbedtls
#else
  DEFAULT_PACKAGES += opkg
#endif

(basically removing the check for CONFIG_USE_APK)

Unfortunately, this doesn't work - still only either ipk or apk packages are built.

I don't know nearly enough about Makefiles to solve this by myself. Maybe there is a Makefile guru here who can advise how to make this work?

1 Like

Not quite sure why you would need both APK and opkg compilation at the same time.
You should always use the branch-specfic toolchain/SDK to compile packages. It is bad practice to try to use same package binaries for multiple branches, as there may be changes in the surrounding environment/scripts/metadata/whatever...

You should have packages for 24.10 and below releases compiled with the respective toolchain/SDK and those support only opkg. Development main branch toolchain defaults to apk.

The only deviation could be opkg for main, but that non-standard config is rare and people using it need to use full toolchain in any case to compile firmware and can then also compile your package by themselves.

So, I wonder why you think that you would actually need this "let's build both" approach.


It is really hard to try to figure out the changes made from the clean pastebin. It would be easier to read from context diffs.

Just including two separate makefiles is likely not enough, as they define similar compilation targets.

Instead of having two separate makefiles, I would try to edit the original makefile and concatenate/duplicate the necessary functions.

But I am thinking that a brute-force approach might be easier: have two SDKs in use for main, one for APK and one for opkg. And compile binaries separately.

I'm not entirely sure what you mean by "branch". If you mean that a separate SDK instance is needed to build packages for each OpenWrt release then there are 2 aspects to this.

First: this makes it very hard to provide updates for a package on older OpenWrt versions. So this would strongly discourage me from supporting anything but the most recent OpenWrt version. I understand that updating a package in the OpenWrt repository for an older version is problematic, but at least I can provide updates via my own Github page. If I have to use a separate SDK for each version then this becomes completely not feasible.

Second: in practice, I haven't encountered any problems with my current approach of compiling opkg packages on a recent-ish SDK and installing them on old'ish systems. Just now I tested the toolchain/SDK in the current snapshot, and the built opkg packages install and uninstall just fine on a system running an OpenWrt snapshot from around 2021, and on a system running OpenWrt 23.05.2. Maybe this works for my project because the packages are fairly simple (just some shell scripts and text files), and it won't work for someone else. But as long as it works for me and no problems are reported, I will continue to use this method because the benefit of providing updates to older systems is way too big to give up easily.

While this may come across as being stubborn, my goal is to better serve people who are using older versions of OpenWrt and to avoid being part of the 'use-latest-or-get-lost' software development culture.

Because my general approach, as explained above, is to support the widest range of systems possible via the Releases page in my Github repo.

In the meantime, it dawned on me that rather than doing the quite fragile modifications to the logic in package-pack.mk, I can simply set the CONFIG_USE_APK variable in the beginning of package-pack.mk. I also figured out that the provides directive needs to be changed. So the diffs are simple:

$ diff include/package-pack.mk include/package-pack-apk.mk

4a5,6
> CONFIG_USE_APK=y
>
215c217
<     $(PKG_INFO_DIR)/$(1).provides $$(PACK_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-pack.mk
---
>     $(PKG_INFO_DIR)/$(1).provides $$(PACK_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-pack-apk.mk
$ diff include/package-pack.mk include/package-pack-ipk.mk

4a5,6
> CONFIG_USE_APK=
>
215c217
<     $(PKG_INFO_DIR)/$(1).provides $$(PACK_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-pack.mk
---
>     $(PKG_INFO_DIR)/$(1).provides $$(PACK_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-pack-ipk.mk
$ diff include/package.mk /tmp/package.mk

139c139,140
< include $(INCLUDE_DIR)/package-pack.mk
---
> include $(INCLUDE_DIR)/package-pack-apk.mk
> include $(INCLUDE_DIR)/package-pack-ipk.mk

When the above diffs are applied, only one package type is built, and the type depends on the order in which the includes are specified in package.mk. If apk is specified last then it builds apk, otherwise it builds ipk. So I think that this is a namespace issue, i.e. same-named functions are defined twice and only the last definition sticks.

Yes. I mean that.
The code base of each release has started to separate at the branching of each release branch. Development happens on main/master, but 24.10 and 23.05 are stuck to the branching moment to some extent. Fixes and minor improvements are backported there, but largely the underlying OpenWrt codebase is from May 2023 or Oct 2024. There are differences in packaging (like apk/opkg), but also on surrounding scripts/metadata whatever. And the underlying musl c library may also be different, which usually breaks binary compatibility of any compiled C programs (which 99% of packages are).

1 Like

So there is no compiling to do, just some packaging.

For one of my packages that is also shell scripts and text files, I use the SDK.
Currently the 23.05.4 SDK and the main/master SDK.

I run both from a script, one after the other and I have a .ipk and a .apk in less than 45 seconds. Perfect for development/testing and the occasional upload to Github for community testers.
The generated .ipk installs and runs just fine on 23.05 and 24.10 with no problems. Earlier versions would be ok too (as it is just shell scripts), but in my case the package depends on nftables so no point.

1 Like

Imagine that it took 25 seconds instead, and all you had to do for this is add a couple lines of code to your script. Isn't it worth it? Personally, while testing, sometimes I have to run the compile command (well, this is how the SDK calls it) quite a few times in a row, and these extra 20 seconds (on my machine this is actually much longer, perhaps extra minute or so) add up rather quickly.

Thanks for the explanation. Any specific advice on how to solve the problem in question (creating both ipk and apk in one "compile" session)?

By the "compile command" I assume you mean make...
Why multiple times? Two is enough for an ipk and an apk, what else do you need?
I did say less than 45 seconds (as a guess), in fact measuring it now, it is 8 seconds for each ie 16 seconds.

But then I have an apkcopy and an ipkcopy pair of scripts that scp to the relevant target test device, so I guess that takes a second or two, then I have to ssh in and install it, so maybe 60 seconds to be up and running is more realistic after all....

Yes, specifically

make $make_opts "package/$p_name/compile"

(direct quote from my script)

Well, because I am human and I make mistakes. I may forget to change the version and the revision, I may introduce a bug, I may make some change in the Makefile which I then need to test, debug and fix.

I do all of that, and find out the errors of my ways when testing, or trying to test. The time taken by the packaging process is insignificant.

My script tars the source from my dev git, calculates the sha256 hash, seds this into the make file then does:
make -j2 V=s package/$packagename/compile
make -j2 V=s package/index

... all in 8 seconds (per package type).

1 Like

Good for you! I guess you are not running this on a 11-year-old CPU and a cheapo old SSD. I do. All my computers are essentially built from scrap (I fix computers for living, so a lot of scrap comes my way). Yep, it's not as fast, but it's free, and it keeps all this stuff away from the landfill.

But also I'm not hyped about having to install, compile and maintain a 2nd SDK when one SDK is perfectly capable of doing the job. Sans the extra time it takes to reconfigure it from apk to ipk and back. That last part is what I'm trying to eliminate here.

BTW what does this command do?

That is what I used to have until 2 years ago, and it took in the region of 30 hours to install and configure the SDK, if I stood the laptop on its side with a fan blowing on the bottom, but still less than 30 seconds to package a script.
Now I have a 2 year old "cheapest, bottom of the range" Asus laptop to do the job.

This generates the packages index file ie packages.adb in the case of apk and Packages.gz in the case of ipk.

Sorry if this is a stupid question but why are these needed and how/where are they used? I don't recall seeing this mentioned in the Developer's guide in the wiki but I may have missed it.

They are used by opkg/apk when

  • Updating the local list of available packages (opkg or apk update).
  • Resolving dependencies when installing or upgrading packages.
  • Providing information for searching or listing packages.

Not needed if you are just copying and installing...

But you are running make -j2 V=s package/index on the machine which is doing the packaging, which I assume is not running OpenWrt? To my understanding, packages.[adb|gz] are automatically updated on the machine running OpenWrt? So I still can't figure out what this does and why you would need that. Sorry again if it's a stupid question.

Yes, it is created in my local package repository.
For example, in the official repositories you can see the file here:
https://downloads.openwrt.org/releases/23.05.5/packages/mips_24kc/packages/

1 Like

So you configure your OpenWrt machines to pull packages from your local repository?

Sometimes yes. But for rapid iteration testing, no. I just run the relevant script to scp the package file to /tmp on the device, ssh and install. I guess I could shave a sliver off the packaging time If I left it out when doing that kind of testing :wink:

1 Like