I also want to state for the record that all of my criticism is directed towards the article, and not towards you Nils.
-- Jason Liu On Sat, Aug 30, 2025 at 8:46 PM Jason Liu <jason...@umich.edu> wrote: > I very strongly disagree with almost everything in the article that you > linked. I believe that the author's entire premise is wrong. They somehow > conflate reproducible builds with the ability to specify version ranges in > the build system. Yes of course you can use the ability to specify version > ranges to create non-reproducible builds, but even right now in MacPorts, > you can easily write Tcl code in a Portfile that can result in > non-reproducible builds. Having version ranges doesn't automatically mean > that all builds are now suddenly non-deterministic and non-reproducible. > > In addition, I would argue that the two examples that they provide, the > one with the conversation between Bob and Alice and the second one with the > trailing comma bug, don't actually have any relation to version ranges at > all. Both of these scenarios occur quite often during the course of both > software development and systems administration work... it can happen _any_ > time software gets upgraded. It even happens in MacPorts right now, as > occasional messages on this very mailing list can attest to. > > I do agree that "1.8.0 - Infinity is a bad version range". But this is > why, if you look back to my examples, I nearly always use a range that has > limits on both ends of the range. In each and every version range example > the author provides, it always shows a "blahblah version AND ABOVE". The > author completely ignores ranges that have limits on both ends. > > I also find their list of suggested solutions to range from (yes, pun > intended) unhelpful to downright bewildering. Don't allow version range? > Well, that is the current situation of MacPorts, so that's unhelpful. Use > version control outputs and copy the binaries for dependencies into your > repo? Wait, what?! Seriously? Look, I know that MacPorts is usually used on > macOS, and Apple loves to "sandbox" everything by always including compiled > dependencies (e.g. libraries) in each and every app bundle, but MacPorts, > and FreeBSD ports collection, and every other *nix-style package management > system, are specifically designed to _avoid_ having copies of compiled > binaries all over the place in each and every project's source code tree. > Copy repos? Why would anyone do this, when the MacPorts project uses git, > which has the ability to reference other repos through the use of git > submodules? > > I also find their statement that "Any dissenting opinion lacks imagination > (and experience)" to be incredibly condescending. Because even what this > statement is referring to, that "there's no such thing as a 100% innocuous > change", doesn't inherently have anything to do with the ability to specify > version ranges. Upgrading 1.8.0 to 1.8.1 causing months of stored data to > be lost has everything to do with managing your own dev group's software > update scheduling, it has nothing to do with being able to specify version > ranges. Software upgrades causing data loss is the very reason why most > professional-level software engineering teams have dev, staging, and prod > servers that are completely separate, in order to prevent this very > scenario from happening. > > The author is even forced to admit, in smaller text as a footnote, that: > "FYI, there actually is one valid reason for using version ranges: to > document/enforce compatibility. This is the intent in package managers like > apt and yum." This is precisely the applicable case for MacPorts: it's a > package manager. > > -- > Jason Liu > > > On Sat, Aug 30, 2025 at 12:17 PM Nils Breunese <br...@macports.org> wrote: > >> I agree that version awareness would be a very helpful concept. All other >> dependency mechanisms that I’ve encountered (Maven, NPM, etc.) have this >> ability. In MacPorts for some ports there are separate ports for different >> major versions, but sadly in 2025 backwards compatibility is still very >> often broken in non-major updates, so being able to depend on a specific >> version would be very welcome. >> >> I do believe supporting version *ranges* for dependencies is a mistake, >> because they break reproducible builds. See e.g. >> https://lucid.co/techblog/2017/03/15/package-management-stop-using-version-ranges >> . >> >> Nils. >> >> Op 30 aug 2025 om 17:38 heeft Jason Liu <jason...@umich.edu> het >> volgende geschreven: >> >> >> [Warning: Apologies ahead of time. This reply message is super long, but >> hopefully everything I've said is relevant.] >> >> Please correct me if I'm wrong here, but it seems like the "mutating >> port" that you talk about would probably be referred to by various *nix >> package management systems as a "metapackage" or a "virtual package". For >> example, a couple of years ago I worked with @Gcenx to modify the MoltenVK >> port to behave like a virtual package, which attempts to select the correct >> versioned subport based on the user's OS version. The unversioned MoltenVK >> port doesn't install any binary itself, it only mutates which versioned >> subport gets specified as the 'depends_run'. >> >> ========== >> >> I have long advocated for moving MacPorts to be a fully version-aware >> package management system. All modern PMSes allow specifying version >> requirements when listing dependencies. I particularly like the robust way >> that Debian implemented it when they rewrote Apt from the ground up for >> version 2.0 ( >> https://www.debian.org/doc/debian-policy/ch-relationships.html), e.g.: >> >> Package: mutt >> Version: 1.3.17-1 >> Depends: libc6 (>= 2.2.1), default-mta | mail-transport-agent >> >> (Here's a more complicated example involving ranges: >> https://askubuntu.com/questions/766846/version-range-in-debian-control) >> >> Even FreeBSD's Ports has implemented the ability to specify dependency >> version requirements ( >> https://docs.freebsd.org/en/books/porters-handbook/makefiles/#makefile-version-dependency >> ): >> >> p5-Spiffy>=0.26:devel/p5-Spiffy >> >> I'm fully cognizant that this would involve a major re-architecting of >> macports-base and probably also the server-side infrastructure, but I >> continue to feel that, at some point, the MacPorts project _must_ implement >> this sort of a feature in order to continue to be viable as a modern PMS >> far into the future. >> >> ========== >> >> Sometimes the single mutating port strategy doesn't work so well. The go >>> port is an example. Many go modules fail to build on older systems because >>> they require a newer go than the latest one that works on the older system. >>> But this wouldn't be solved by switching to multiple versioned go ports. >>> Each go module port would still need to know its minimum go version which >>> it currently doesn't. >> >> >> I believe this problem would be completely solved by having a >> version-aware PMS. >> >> First, let's assume that all ports are versioned, because we have a >> version-aware PMS. When an older system is asked to install a particular go >> module, it will automatically know that it needs to install an older >> version of the module, because the module itself is an unversioned virtual >> package that selects the go module version based on OS version. The >> versioned (sub)port of the module will attempt to install the go compiler >> as a dependency; this dependency line in the Portfile has a version range >> specified, i.e. 'depends_build: go (>= 2.6.1 && <= 3.1.7)', which means >> that the newest version of go within that version range will get added to >> the dependency tree. Thus, an old version of go that is compatible with the >> old version of the module will get installed, followed by the old version >> of the go module that is compatible with the old OS version. >> >> The separate versioned ports strategy also doesn't always work well >>> because it takes continual effort to keep them in sync. >> >> >> Not necessarily. If you consider the versioned ports to basically be >> immutable after they have been fully packaged, then you can consider them >> to be historical archived snapshots of each version of the port. (Okay, >> immutable is not really the right word, because Portfiles might need to be >> altered with minor fixes and revbumps.) Really the only thing that would >> take continual effort to keep in sync would be the Portfiles for the >> top-level "unversioned" virtual packages. >> >> If we go back to the go and go-module example, then this is what the >> various Portfiles would look like (I'm going to use pseudocode here, please >> don't hate me): >> >> In the go module's unversioned virtual package >> (ports/go/go-module/Portfile): >> >> if {${os.major} <= 15} { >> depends_run go-module (>= 14.1.7 && <= 16.2.2) >> } elseif {${os.major} == 16} { >> depends_run go-module (>= 16.3.0 && <= 17.4.4) >> } else { >> depends_run go-module (>= 18.0.0) >> } >> >> In the go module's OLD versioned (sub)port >> (ports/go/go-module/16.2.2/Portfile): >> >> depends_build go (>= 3.0.0 && <= 4.3.7) >> >> In the module's NEW versioned (sub)port >> (ports/go/go-module/18.0.0/Portfile): >> >> depends_build go (>= 6.0.0 && <= 7.1.4) >> >> As you can see, because the Portfile for go-module v16.2.2 exists at the >> same time as the Portfile for go-module v18.0.0, and the valid version >> ranges for each version of the go module will always be specified in the >> versioned (sub)port's Portfile, installing that particular version of the >> go module will always specify a valid version of the go compiler. >> >> Note that this also elegantly takes care of your example of >> >> For example, if I later figure out a way to offer version 5 to some older >>> OS versions, how does the user who installed cliclick4 learn of that? >> >> >> With a version-aware PMS, let's say that you figure out how to get v5.0.0 >> of the go compiler to work with Darwin 15. Then in the go module's old >> versioned (sub)port (ports/go/go-module/16.2.2/Portfile), you would update >> it to be this: >> >> revision 5 # revbump >> depends_build go (>= 3.0.0 && <= 5.0.0) >> >> It wouldn't disturb go-module v18.0.0 in any way, because both of >> their Portfiles exist at the same time in the ports tree. >> >> I know what you're thinking. The logical next question would be: "What >> about all of the Portfiles for go-module from versions 14.1.7 through >> 16.2.1? Would those now also need to be changed so that the 'depends_build' >> line for go is also 5.0.0?" And my answer to that would be: You could if >> you wanted to, but I don't think it would be absolutely necessary. Because >> what's the likelihood that someone would want to use go-module v16.2.1, or >> v15.y.z, or v14.1.7, when v16.2.2 is available? >> >> On the one hand, in the scenario where you don't change those other >> Portfiles, they would still work fine, it's just that they would still be >> dependent on the go compiler being (>= 3.0.0 && <= 4.3.7). It wouldn't >> break anything, it's just that anyone who specifically wants to install one >> of those particular older versions of the module wouldn't receive the >> benefit of your efforts on getting go-compiler v5.0.0 to work on Darwin 15. >> On the other hand, if you wanted to risk it, you could mass update all of >> the Portfiles for go-module v14.1.7 through v16.2.2 to use go-compiler >> v5.0.0 by using sed or awk commands. >> >> ========== >> >> I think one trade-off of a fully version-aware PMS might be that it could >> explode the amount of storage needed, if you were to start retaining all of >> the pre-compiled binaries for versioned ports. But, other PMSes seem to be >> able to handle this somehow, so I'm sure there must be a way to balance the >> trade-offs. Perhaps you could have some way to specify which versions of >> old pre-compiled binaries to keep on the servers, and leave the other >> unused versioned ports with only the control files (i.e. the Portfile) >> present, forcing the user to build no-longer used versioned ports from >> source locally on their own machines. >> >> -- >> Jason Liu >> >> >> On Fri, Aug 29, 2025 at 10:27 AM Ryan Carsten Schmidt < >> ryandes...@macports.org> wrote: >> >>> Should we continue to allow changing the port version based on OS >>> version? Currently we do this in several ports e.g. to offer the last >>> compatible version to old OS versions. Josh brought up in a ticket that we >>> should consider not doing that, and instead create separate versioned >>> ports. I thought we should discuss because there are pros and cons to each >>> approach and currently we have a mix of strategies. >>> >>> For example a user who wants cliclick just installs the port. It picks >>> the right version for the OS version. This is simple for the user. The >>> alternative is that a user of an old OS would have to know to install a >>> hypothetical cliclick4 instead. And the situation becomes complicated if >>> the criteria change. For example, if I later figure out a way to offer >>> version 5 to some older OS versions, how does the user who installed >>> cliclick4 learn of that? >>> >>> But such ports that mutate to fit the environment are more complex to >>> write and maintain. And we have to remember that we can only change the >>> version (and any other property that goes into the portindex) based on the >>> segmentation of our server-side portindexes. We maintain separate indexes >>> for each OS version/arch combination so we can change a port version based >>> on OS version or arch but not based on other criteria like Xcode version. >>> >>> Sometimes the single mutating port strategy doesn't work so well. The go >>> port is an example. Many go modules fail to build on older systems because >>> they require a newer go than the latest one that works on the older system. >>> But this wouldn't be solved by switching to multiple versioned go ports. >>> Each go module port would still need to know its minimum go version which >>> it currently doesn't. Or, depending on how common it is for modules to >>> require newer go versions, we could dispense with the idea of go supporting >>> old systems entirely. >>> >>> The separate versioned ports strategy also doesn't always work well >>> because it takes continual effort to keep them in sync. The postgresql >>> ports for example are all a little different from one another because at >>> various times a fix or a reformat only got applied to one of them but not >>> the others. >>> >>> >>>