<div dir="ltr"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>It's my understanding, for example, that Homebrew allows the user to
request to install the git master of a particular Homebrew formula, in
effect allowing the user to (try to) install a version of the software
that was never previously attempted by the formula's creator. This is
what I was afraid was part of what was being proposed, and something I
would not support adding to MacPorts.</div></blockquote><div><br></div><div>I'm not that familiar with Homebrew, so this is the first time I'm hearing about this sort of capability. None of the other PMSes that I have experience with can do this. The idea of being able to (try to) install a version of a package that doesn't even exist in a repository's metadata database sounds insane to me.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>If we continue the liberal approach with dependencies, then we begin
from the assumption that a port can use any version of a dependency.
Let's say libpng declares it can use any version of zlib. Then years
later zlib 2 is released and it is incompatible, breaking all versions
of libpng we've published. There's now a lot of work to go edit each
previous libpng to indicate that it requires zlib < 2.</div></blockquote><div><br></div><div><span style="font-family:monospace">#!/bin/bash<br></span></div><div><span style="font-family:monospace">for f in libpng/Portfile-*; do</span></div><div><span style="font-family:monospace"> sed -i -e s/(port:zlib.*)$/\1 < 2/ $f</span></div><div><span style="font-family:monospace">done</span></div><div><br></div><div>I'm probably missing some backslashes, but I think you probably get the idea. That doesn't seem like that much work to me.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>Alternatively, suppose we use the conservative approach and declare that
libpng requires zlib < 2. Or zlib < 1.3. Or zlib < 1.2.12. We
have no way to know in what future version of zlib, if any,
incompatibility might arise. Suppose we declare compatibility with zlib
< 2, and years later zlib 2 comes along and maintains perfect
backward compatibility with zlib 1. Then our libpng would continue to
use the old zlib 1, even though it would work with zlib 2, thus making
users use outdated dependencies unnecessarily and possibly exposing them
to bugs that have already been fixed in newer versions. And the
solution then is to do a lot of work to go edit each previous libpng to
update its zlib compatibility list.</div></blockquote><div><br></div><div>A sed statement wrapped inside a for loop would probably also help in this case.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>Either way, it's signing all port maintainers up for a lot of work.
(Many port maintainers already don't do the currently expected amount of
work to maintain their ports.) And all for the benefit of other ports
being able to declare that they require an old version of libpng. We did
have this problem once before, when some old ports were not compatible
with libpng 1.4 when it came out. Rather than introducing a libpng12
compatibility port, we did the right thing and fixed other software to
be compatible with libpng 1.4. If we had used a libpng12 port instead,
we likely would never have noticed if and when those ports gained libpng
1.4 compatibility and would be forcing libpng12 on users unnecessarily
for years to come.</div></blockquote><div><br></div><div>However, I feel like this would be a good example of why having multiple versions of a package's portfile available could be beneficial. Let's say libpng 1.4 came out and it broke a bunch of old ports. If previous versions of the portfile were available, like what I showed in my example of the Blender Debian package, then it would be very easy to simply remove the libpng 1.4 portfile from the repository, and all of the ports that were broken would suddenly be fine again, because they would just go back to using whichever portfile for libpng was already there. No need to increase the epoch inside the portfile to try to back-rev to an older version of libpng, no need to create a libpng12 compatibility port. And the port maintainers would be able to go right back to working on fixing ports locally on their test machines, without having to worry that the actual global ports tree had been broken for users. (By the way, this is why most flavors of Linux have multiple release channels, typically "stable", "testing", and "unstable". Each release channel has its own PMS repository, to avoid the situation where the global repository that users use gets polluted due to a package incompatibility.)<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>And all for the benefit of other ports
being able to declare that they require an old version of libpng.</div></blockquote><div><br></div><div>If there's really such little benefit, then why are we even having such a discussion? And why are there other PMSes which have this ability? Because they enjoy torturing their package maintainers with lots of busy work? Somehow I doubt it.... They wouldn't have gone to all the trouble of adding such a feature unless it was able to reduce the amount of work they had to do in some way.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">This is one place where MacPort's activate/deactivate would really
shine: If someone wanted to install an old package, MacPorts would first
deactivate all of the dependencies that were too new, and then install
the old versions of the dependencies that are compatible with the old
package.<br></blockquote>
<br>
If all versions of a port install to the same location on disk as they
do currently and only one version of a port can be active at a time,
then your proposal here -- "deactivate all of the dependencies that were
too new" -- will break all the ports that depend on the newer
dependencies.</div></blockquote><div><br></div><div>Yes, that is precisely what I am proposing: that doing so would break all ports that depend on the newer dependencies. This is exactly what happens in PMSes like APT and YUM. And very similar to what happens if you were to do a "port deactivate libpng", the PMS will provide a helpful warning message telling you that the following list of packages will be broken if you proceed... do you still wish to continue? [y/N].</div><div><br></div><div>I realize that there are probably many people screaming at me through their monitors right now saying, "That's a TERRIBLE idea! Why would we want to allow users to break a bunch of stuff using our system?!" Other PMSes will warn you when you are about to do something like that, but the fact is that they will still allow you to do it if you insist on continuing. Again, I point out that this is where MacPorts' activate/deactivate system could really shine, because MacPorts still retains on local disk the other version that was already installed, and you can switch back and forth between versions using "port activate". This is still much better than APT, which will simply uninstall the newer version of the dependency and replace it with the older version (i.e. you can only have one copy of a package installed on the system at one time).<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>For us, the single product we are providing is MacPorts, which has a
very broad focus: ten thousand software packages intended to work
together. That goal isn't always met: sometimes updating one port breaks
another, until we fix it. But treating each of those ten thousand ports
as a separate top-level product that needs its own world of
dependencies would be an unfathomable disaster.<br></div></blockquote><div><snip> <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>
Don't forget also that separate libraries would be loaded into memory
separately. If you have a zillion copies of gettext and libpng
installed, one for each port that uses them, and then you want to use
something huge like Qt or WebkKt, then you're loading hundreds of copies
of gettext or libpng into memory.</div></blockquote><div><br></div><div>But isn't this precisely what NPM does? Apple's take (and possibly NPM's too) would be to say, "storage and memory are cheap these days". I don't know where all this supposed cheap stuff is available for sale, but I certainly don't have the money to just go out and upgrade the storage and memory on all of my computers.<br></div><div><br></div><div>I hope it's clear by now, but just in case we're still in "nothing is obvious" territory, let me clearly state that my personal preferences generally lean more towards the side of the *nix-like philosophy of "sharing/reusing as many resources as possible is better". However, I think you probably have to admit one thing about NPM is that its packages are very self-contained.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>I thought it was being proposed that all versions of a port would
install to different locations on disk so that many could be active
simultaneously, which would avoid the one problem while causing many new
ones, most of which we haven't begun to discuss yet.</div></blockquote><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>Let me try an example.<br>
<br>
Suppose I have zlib installed at:<br>
/opt/local/port/zlib/1.2.11/{bin,include,lib}</div></blockquote><div><snip></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div>So I hope this npm-style hierarchy is not what anyone is proposing.</div></blockquote><div><br></div><div>No, I certainly hope nothing that I had said before was giving the impression that this is what I was suggesting, and in my mind this would be a bad idea. However, I think it's impossible to deny that all of the PMSes mentioned so far, APT, YUM, NPM, Yarn, etc. have all been quite successful, and they all give users the ability to install specific versions of packages.<br></div><div><br></div><div><div dir="ltr" data-smartmail="gmail_signature"><div dir="ltr"><div>-- </div><div>Jason Liu<br></div></div></div></div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Oct 4, 2020 at 5:34 PM Ryan Schmidt <<a href="mailto:ryandesign@macports.org" target="_blank">ryandesign@macports.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
<br>
On Oct 4, 2020, at 14:09, Jason Liu wrote:<br>
<br>
>> Where by "this" you again mean the ability to specify a previously-available version, not an arbitrary version that was never in a portfile.<br>
> <br>
> Yes, of course. If you look at the folder for the Blender package on the Debian repo<br>
> <br>
> <a href="http://deb.debian.org/debian/pool/main/b/blender/" rel="noreferrer" target="_blank">http://deb.debian.org/debian/pool/main/b/blender/</a><br>
> <br>
> from my previous example, there are many versions of Blender that were skipped. For instance, they skipped version 2.82 and went from 2.79b right to 2.82a. And they skipped versions 2.83.0-2.83.4, and went from 2.82a right to 2.83.5. Obviously, there would be no way for a user to try to install Blender version 2.83.2 using apt-get, because that version was never packaged (i.e. spec files don't exist) for that particular version of Blender.<br>
<br>
Let's try not to say "of course". Nothing is obvious in this discussion. Let us be very clear at every step what we are proposing so that we can have a meaningful discussion.<br>
<br>
It's my understanding, for example, that Homebrew allows the user to request to install the git master of a particular Homebrew formula, in effect allowing the user to (try to) install a version of the software that was never previously attempted by the formula's creator. This is what I was afraid was part of what was being proposed, and something I would not support adding to MacPorts.<br>
<br>
<br>
> The examples were meant to illustrate the fact that it's possible to restrict the dependency versions when installing an old package. By specifying maximum versions for all of a package's dependencies (by using something like <=2.4.0 or <2.4.0), it would guarantee that the old package would never have to be built against new dependencies, because if a user were to install the old package, it would also install older versions of the dependencies.<br>
<br>
I understand that it would be possible to define a syntax for specifying dependency versions and that MacPorts base could be enhanced to parse it and even to find and download the right version of the portfile and to install different versions of ports to different locations.<br>
<br>
The problem is that if you are currently compatible with the latest version of a dependency, you do not know in advance whether you will be compatible with the next version of that dependency.<br>
<br>
So you can either be conservative or liberal. MacPorts has tended to be liberal in these matters, assuming that a port can build on any architecture, any OS version, with any compiler. Once it is discovered that it can't build on some subset of those, restrictions can be added. supported_archs can be used to rule out incompatible architectures. compiler.blacklist can be used to rule out incompatible compilers. Ports can indicate which OS versions they're compatible with, though not as elegantly as I would like.<br>
<br>
If we continue the liberal approach with dependencies, then we begin from the assumption that a port can use any version of a dependency. Let's say libpng declares it can use any version of zlib. Then years later zlib 2 is released and it is incompatible, breaking all versions of libpng we've published. There's now a lot of work to go edit each previous libpng to indicate that it requires zlib < 2.<br>
<br>
Alternatively, suppose we use the conservative approach and declare that libpng requires zlib < 2. Or zlib < 1.3. Or zlib < 1.2.12. We have no way to know in what future version of zlib, if any, incompatibility might arise. Suppose we declare compatibility with zlib < 2, and years later zlib 2 comes along and maintains perfect backward compatibility with zlib 1. Then our libpng would continue to use the old zlib 1, even though it would work with zlib 2, thus making users use outdated dependencies unnecessarily and possibly exposing them to bugs that have already been fixed in newer versions. And the solution then is to do a lot of work to go edit each previous libpng to update its zlib compatibility list.<br>
<br>
Either way, it's signing all port maintainers up for a lot of work. (Many port maintainers already don't do the currently expected amount of work to maintain their ports.) And all for the benefit of other ports being able to declare that they require an old version of libpng. We did have this problem once before, when some old ports were not compatible with libpng 1.4 when it came out. Rather than introducing a libpng12 compatibility port, we did the right thing and fixed other software to be compatible with libpng 1.4. If we had used a libpng12 port instead, we likely would never have noticed if and when those ports gained libpng 1.4 compatibility and would be forcing libpng12 on users unnecessarily for years to come.<br>
<br>
<br>
> This is one place where MacPort's activate/deactivate would really shine: If someone wanted to install an old package, MacPorts would first deactivate all of the dependencies that were too new, and then install the old versions of the dependencies that are compatible with the old package.<br>
<br>
If all versions of a port install to the same location on disk as they do currently and only one version of a port can be active at a time, then your proposal here -- "deactivate all of the dependencies that were too new" -- will break all the ports that depend on the newer dependencies.<br>
<br>
I thought it was being proposed that all versions of a port would install to different locations on disk so that many could be active simultaneously, which would avoid the one problem while causing many new ones, most of which we haven't begun to discuss yet.<br>
<br>
<br>
>> Despite the bloat it might work for that use case, but some npm module developers unnecessarily restrict the versions of dependencies they're compatible with, meaning that you get an unnecessarily outdated version of that dependency, even though a newer version of the dependency would work and might fix some bug you're experiencing. And this ballooning of dependencies can use a tremendously larger amount of disk space than if just a single compatible copy of each dep had been installed. We already have users complaining about unnecessary dependencies and unnecessary disk usage; this would just make it worse.<br>
>> <br>
>> And do you really want to install a separate copy of gettext for each dependency that uses it?<br>
> <br>
> Well, let's be frank here. This is the "Apple way". Other than the system frameworks, Apple has always strongly encouraged that software keep all of its bits and pieces inside of its application bundle in the /Applications folder. This has traditionally meant that multiple apps would have their own copy of the same gettext library in each of their application bundles; because Apple has never said "hey developers, put all of your shared libraries in this /usr/local or /opt/local folder". Instead, they've always gone the route of "each and every app only gets to play in its own sandbox; you should bring all of your own toys (i.e. libraries) to your own sandbox... no sharing of toys! (other than the ones Apple provides and controls, i.e. system frameworks)"<br>
> <br>
> I'm not saying that I agree with Apple's viewpoint, but I do find it a bit odd that users would use a *nix-like argument of "share as much as possible to reduce bloat" to complain about something (MacPorts) that lives on an Apple Corporation-made system, where "share as little as possible" is the norm. MacPorts is already a shining example of sharing and reusing the same libraries on a system that highly discourages such behavior.<br>
<br>
What you say about the "Apple way" of doing application bundles is true, and it in no way follows that we should do the same in MacPorts.<br>
<br>
Consider an application like Firefox, developed by an organization, using various open source libraries, which are all included in the application bundle. But that's it. There's only one copy of each dependency because the developers know that they are creating a single product and that all the dependencies need to work together. They can hold back updating certain dependencies if they know it will cause problems for other libraries they use. They can exclude manpages, programs or other files they don't need and distribute only what's needed for their specific use case within Firefox. They have a narrow focus.<br>
<br>
For us, the single product we are providing is MacPorts, which has a very broad focus: ten thousand software packages intended to work together. That goal isn't always met: sometimes updating one port breaks another, until we fix it. But treating each of those ten thousand ports as a separate top-level product that needs its own world of dependencies would be an unfathomable disaster.<br>
<br>
Installing all ports to a different top-level prefix for that port name and version, which is what I thought we were talking about initially, is slightly less disastrous. For example, if we had gone the other way with the libpng example above, we could have prefixes /opt/local/port/libpng/1.2.57 and /opt/local/port/libpng/1.6.37, each of which would contain the bin, include, lib, etc. directories for that version of libpng. Then the majority of ports that are fine with 1.6 could use that and those that need 1.2 could use that, and then that would only be two copies of libpng installed instead of a thousand.<br>
<br>
Don't forget also that separate libraries would be loaded into memory separately. If you have a zillion copies of gettext and libpng installed, one for each port that uses them, and then you want to use something huge like Qt or WebkKt, then you're loading hundreds of copies of gettext or libpng into memory.<br>
<br>
<br>
>> But you haven't addressed how you could build historical "Portfiles" on a newer OS or compiler version when they would not have been written to account for the new restrictions imposed by that newer OS or compiler version, and we go back to my supposition that a portfile maintainer would be asked to keep updating and patching ever old versions of their portfile.<br>
>> <br>
>> How would you address the "implicit declaration of function" problem I mentioned? A new version of Xcode, 12, has come along and shown us that a zillion of our ports, such as php, do not include the headers that declare the functions they are using. We must fix this for compatibility with future Macs. <br>
>> <br>
>> This is just an example of the type of problem that comes up all the time. Yosemite required tons of patches because many build systems misinterpreted 10.10 as 10.1. macOS 11 is requiring tons of patches because many build systems expected the macOS major version to remain at 10 forever. Many build systems need patches now for ARM support. etc. etc. etc.<br>
> <br>
> In this particular case, I believe that the Xcode version, and even the macOS version, should be counted as just another type of dependency... one that happens to be required in every single portfile. If these (required) variables also had the ability to specify version ranges, we could effectively cap each version of a portfile with a maximum compatible version of macOS and Xcode, which could be raised as compatibility patches are added for newer macOSes and Xcodes.<br>
> <br>
> macos_version_compatibility <=10.15.7<br>
> (I prefer to use Darwin version)<br>
> <br>
> darwin_version_compatibility <=19<br>
> (this portfile would be compatible up to Catalina, but not yet compatible with macOS 11)<br>
> <br>
> xcode_version_compatibility >=8.2.1 && <=12beta2<br>
> (or whatever)<br>
<br>
Agreed we should have a way to specify OS version compatibility with a variable. It is a long-standing wish. There is a ticket open about maybe using the currently-unused platforms variable for this purpose. Many ports do indicate OS compatibility but with manually-written code blocks.<br>
<br>
We already have the supported_archs variable for indicating architecture support.<br>
<br>
Xcode version compatibility has historically not been so interesting; it's usually the compiler and SDK version that's relevant. We already support restricting which compilers can build a port, using compiler.blacklist. I'm working on something similar for SDK versions. We do already have the xcodeversion portgroup for restricting Xcode versions, but it is not usually necessary and it is old and does not account for the fact that the command line tools are now separate from Xcode and that one might want a way to specify "Xcode < 12 or the command line tools corresponding to Xcode < 12". For completeness with other version checking variables this capability probably belongs in MacPorts base, but the same can be said for many of the portgroups.<br>
<br>
<br>
> The benefit of this would be as follows. Let's say that we went to a system of having one portfile for each version of a particular package. Let's also say that "somepackage" was currently on version 2.4.3, and it is not compatible with macOS 11. If the upstream authors of "somepackage" were to add patches that made it compatible with macOS 11 and released it as 2.4.4, then the new portfile for 2.4.4 would increment darwin_version_compatibility <=20. However, the portfile for 2.4.3 wouldn't need to be changed at all, because it wasn't, and never will be, compatible with macOS 11. If a port maintainer were to come along and backport the macOS 11 patches into the 2.4.3 port, then they could update the 2.4.3 portfile to also be compatible with darwin <=20. A similar concept applies to Xcode version, ARM support, etc.<br>
> <br>
> Thus, the idea of keeping a separate portfile for each version of a package would answer the question of "how you could build historical "Portfiles" on a newer OS or compiler version", because the answer is: you never would. Once a "Portfile-2.4.3" was pegged to a maximum of Catalina, it would potentially never need to be updated again, because it is not compatible when macOS 11 gets released. Our new "port" command would be able to indicate to users on a macOS 11 machine that only "Portfile-2.4.4" was available, because the old historical "Portfile-2.4.3" isn't compatible with macOS 11.<br>
> <br>
>> This is all work that we are here volunteering to do to improve our current port collection, but asking everyone to retroactively also fix these problems in all older versions would not be acceptable. We don't have a sufficient number of contributors to keep our current collection of ports up to date, so we certainly don't have enough contributors to keep all previous versions of our collection working as well.<br>
> <br>
> This is why it would be important in a "new" MacPorts to be able to cap old versions of a portfile with a maximum compatible version of stuff. ("stuff" meaning dependencies, macOS version, Xcode version, etc.) This would allow older versions of portfiles to slowly fade into obsolescence as their maximum compatible versions of dependencies become more and more out-of-date. There would be no need to keep retroactively updating them once they've been capped with maximum compatible versions.<br>
> <br>
> If compatibility patches can be backported, then great, but if they can't be for any reason, then that's fine too, the old versions of the portfiles will continue to work for older versions of macOS/Xcode. But in my opinion that would be a much better situation than what exists now, where there's only a single Portfile (and a single version of the package) for all users on all versions of macOS. However, I once again reiterate that I also believe it would require a massive amount of effort to implement this new regime.<br>
> <br>
>> You might even have to compile each of those copies from source, since each copy is going to a different location, and MacPorts binaries are not designed to be relocatable. Making our binaries relocatable is something I'm interested in but that is also a large challenge.<br>
> <br>
> MacPorts binaries aren't designed to be relocatable, because MacPorts, in my opinion, operates under a very *nix-like philosophy of sharing and reusing resources. This is also why many macOS apps are relocatable: because they don't share resources with anything else, and thus can play in their own sandbox. Regardless of where you relocate this sandbox to, there is very little chance of the app breaking when you move it, because all of its toys (like the gettext discussed above) move along with it. In many cases, sharing/reusing resources and relocatability are direct trade-offs and in opposition to one another. (Obviously, things like @rpath, @executable_path, @loader_path, etc. were designed to help break this linkage/trade-off.)<br>
<br>
All of this is true and none of it addresses the point I was making.<br>
<br>
In this paragraph I was responding to the comparison to npm, a package manager using a hierarchical dependency scheme, or whatever you want to call it, and why it would not work well for us, as far as I can tell.<br>
<br>
Let me try an example.<br>
<br>
Suppose I have zlib installed at:<br>
/opt/local/port/zlib/1.2.11/{bin,include,lib}<br>
<br>
Now suppose I want libpng and it installs to:<br>
/opt/local/port/libpng/1.6.37/{bin,include,lib}<br>
It depends on zlib, and so as not to conflict with anyone else's zlib it installs its own copy:<br>
/opt/local/port/libpng/1.6.37/port/zlib/1.2.11/{bin,include,lib}<br>
<br>
Now I want to install gd2 and it installs to:<br>
/opt/local/port/gd2/2.3.0/{bin,include,lib}<br>
It depends on zlib so it installs:<br>
/opt/local/port/gd2/2.3.0/port/zlib/1.2.11/{bin,include,lib}<br>
It also depends on libpng so it installs:<br>
/opt/local/port/gd2/2.3.0/port/libpng/1.6.37/{bin,include,lib}<br>
and:<br>
/opt/local/port/gd2/2.3.0/port/libpng/1.6.37/port/zlib/1.2.11/{bin,include,lib}<br>
<br>
We now have lots of extra disk usage on the user's system, lots of extra memory usage for loading all those redundant libraries, and we have problems regarding how did the above get installed: there are a few possibilities:<br>
<br>
1. The user had to build everything from source because we don't have binaries. That's a step back from where we are now. Users like binaries.<br>
2. We had to build each of those permutations of libpng, zlib, and everything else on our build server and distribute each of those specific binaries that are tied to those locations. That's a step back from where we are now in terms of wasting server build time and disk space.<br>
3. We build just one libpng, zlib, etc. on our build server and we have to figure out how to make the packages relocatable. This is an interest of mine but we've done nothing yet to try to make this work. Note that just the task of identifying all Mach-O files and fixing the install_names and library linkage is easy; dylibbundler shows us it can be done. The hard part is figuring out where else the prefix might be baked in and how to solve it. Even MacPorts base itself is not relocatable; see <a href="https://trac.macports.org/ticket/56204" rel="noreferrer" target="_blank">https://trac.macports.org/ticket/56204</a> for why this can be a tougher problem than a simple find-replace in the destrooted files.<br>
<br>
So I hope this npm-style hierarchy is not what anyone is proposing.<br>
<br>
</blockquote></div>