# How to override port Makefile to point to local directory?



## patmaddox (May 16, 2022)

Edit: Found a clean solution

---

I am writing software that I would eventually like to install on FreeBSD. I'm currently stuck trying to write a Makefile that will point to my local git dir, so I can build and install it while I'm developing. All of the finished Makefiles reference tar packages, which makes sense. But when I'm actively developing, I don't want to commit the code, push it, have it built, update the makefile with the commit, and make.

Does anyone know of a way to override options to a port's Makefile to point to a local directory on disk? I want to run "sudo make install" and "make package" from my development directory, using the same Makefile that would be in the ports tree.


----------



## zirias@ (May 16, 2022)

A port Makefile doesn't actually build your software, your software is expected to have its own build system (could be `make` as well, and if it's FreeBSD-specific, you can make use of the `make` library in /usr/share/mk, or could be anything else like e.g. cmake/ninja...). The port Makefile automates the whole process of fetching, configuring/building/staging (by calling into the software's build system), and packaging the software.

So, just postpone writing a port. Use whatever build system you like and install your software directly (e.g. `make install`) while you're developing it.


----------



## patmaddox (May 16, 2022)

Yes and I want to use poudriere to build and package it... which I believe requires making a port Makefile, correct?


----------



## zirias@ (May 16, 2022)

Yes. But that's not what you'd do during development. The requirement for a distfile (tarball, the ports framework will also verify a checksum on it) is just one reason. Another one is that a port always does a clean build. During development, you typically only want to build what changed. (I could continue here, ports are expected to install stripped binaries without debugging symbols, another thing you don't want during development, ....)

Just use your own build system until you're happy enough with your software to think about a release and then write a port for it.


----------



## patmaddox (May 16, 2022)

Yeah I'm definitely missing something here.

fwiw I have already set up a CI pipeline that will build a git commit using poudriere. I just want to speed up the process by not having to commit, push, wait for it to build. I want to build a package locally, and if it all looks good, I'll commit and push.



Zirias said:


> Use whatever build system you like and install your software directly (e.g. `make install`) while you're developing it.



I can write a `make install` that just copies the files where I want them... but I don't think I get a `make deinstall` for free, do I? Plus then I'll be duplicating that `make install` in the port Makefile.

My understanding of ports is that I install to ${STAGEDIR}, and then ports can make a package out of it, deinstall it, check for conflicts, etc.

Packaging stuff is new to me. I've never written a `make install` in my life. If it helps, I'm using elixir and building a mix release. I can confirm that my app generally works. The next thing is making sure it works cleanly on FreeBSD - installing things to the right dirs, creating a symlink in /usr/local/bin, etc. I have figured out how to write a Makefile that will build the app using `mix release` and install everything.

I can quickly change my app's code and test it locally. What I can't do is quickly check that the FreeBSD package build works.

I did come up with a script that zips my local dir into the dir & zip structure that ports expects, so at least when ports checks DISTFILES it will get that instead of fetching it from GitHub.


----------



## zirias@ (May 16, 2022)

patmaddox said:


> I can write a `make install` that just copies the files where I want them... but I don't think I get a `make deinstall` for free, do I? Plus then I'll be duplicating that `make install` in the port Makefile.


No. A port's `make stage` will call your `make install` if your own build system is based on `make`. In that case, your Makefiles are expected to follow the common practice of respecting `DESTDIR`, the port will set it to the location of the staging dir.

Sure, if you want a `make deinstall`, you'd have to write it yourself.



patmaddox said:


> Packaging stuff is new to me. I've never written a `make install` in my life.


Uhm, another reason to postpone that IMHO.

Of course, a port Makefile _could_ have its own install recipes. But the preferred way is to just call what upstream provides. So, the build system of your software _should_ provide some way to do an install. And installing in a different directory should be supported. For `make`, the convention to support this is `DESTDIR`, for other build systems, it could be different.


----------



## patmaddox (May 16, 2022)

Zirias said:


> Sure, if you want a `make deinstall`, you'd have to write it yourself.



What's the point of that, when ports provides install / package / deinstall functionality?



Zirias said:


> Uhm, another reason to postpone that IMHO.



Well I want to learn how to do it some time... and now's the time.



Zirias said:


> Of course, a port Makefile _could_ have its own install recipes. But the preferred way is to just call what upstream provides. So, the build system of your software _should_ provide some way to do an install. And installing in a different directory should be supported. For `make`, the convention to support this is `DESTDIR`, for other build systems, it could be different.



Everywhere I've worked, the convention is to build the app, stick it in a dir in a docker container, and have a startup script that calls the known path to the built app. If you want to call that "installing", then sure – but it's a far cry from the cleanliness that FreeBSD's package system provides. In other words, I'd say that we have a solid _build_ system, but are completely lacking an _install_ system.

I'm not trying to make some general purpose `make install` that will run on every OS out there. I want to build my app (using mix release) and then install it to a FreeBSD system. Ports provides a mechanism to do exactly that. Why would I partially re-implement that behavior myself? With ports I can `make install && make deinstall` and end up with my system in exactly the same state, have dependency and conflict checking, etc.

There's a really good chance I'm missing something obvious, but creating a separate install process seems odd to me. I can build my app, I can write a ports Makefile to install the app to FreeBSD... so why not point the Makefile at my working directory vs fetching it from GitHub?


----------



## zirias@ (May 16, 2022)

patmaddox said:


> What's the point of that, when ports provides install / package / deinstall functionality?


Your own convenience during development for example. Or convenience for the few users of your software that don't use any packaging system. Yes, that's completely optional.



patmaddox said:


> I'm not trying to make some general purpose `make install` that will run on every OS out there.


Well, that's what's typically expected from opensource software source packages. And it's expected to be able to install to a different base dir which is for example used by packaging (by FreeBSD ports, but of course also by lots of package building systems for e.g. Linux).

I repeat myself here: you don't _have_ to use `make` here, there are other systems (also supported by the ports framework). And again, it's possible to create a port from software that doesn't provide anything for installation. But that's more complex and definitely not the preferred way.


----------



## patmaddox (May 16, 2022)

I really don't care about writing something that will work out of the box on other systems. My objective is simple: `pkg install myapp` on FreeBSD. That's it. I am building services that are 100% intended to run on FreeBSD. `pkg install myapp`, `service start myapp`, `/usr/local/etc/myapp.d/myapp.conf` etc.

I'll repeat myself here as well: I can build my software, and I can package it with Poudriere. I'd like to bypass the cycle time there. I have it built, and I have a working Makefile for packaging and installing it. I just want to say "get the source from this directory instead of GitHub."


----------



## zirias@ (May 16, 2022)

If it's specifically for FreeBSD, the `make` lib in /usr/share/mk (e.g. bsd.prog.mk for program binaries) already provides `install` targets, no need to write them yourself in that case.

But doing it manually in the port Makefile is discouraged. One reason is the reason you're asking that question in the first place: you can't use a port during development in a sane way. (edit: and repeating what you *want* won't help much, ports are not designed to be used during software development, so what you want just isn't possible. What I'm telling you is the correct way to do it...)

And btw, if it only has to work on FreeBSD, there's nothing to gain in terms of effort. You have to write some rules for installation, be it in your local Makefile or in your port's Makefile.


----------



## T-Daemon (May 16, 2022)

patmaddox said:


> I just want to say "get the source from this directory instead of GitHub."


I understand this as follows: You want to fetch your applications source code, _compressed in a file_, from a local directory.

You can define that location with the `MASTER_SITES` variable, followed by the file protocol (`file:///`) in the Makefile. That protocol is supported by fetch(1).


----------



## patmaddox (May 16, 2022)

Zirias said:


> But doing it manually in the port Makefile is discouraged. One reason is the reason you're asking that question in the first place: you can't use a port during development in a sane way. (edit: and repeating what you *want* won't help much, ports are not designed to be used during software development, so what you want just isn't possible. What I'm telling you is the correct way to do it...)
> 
> And btw, if it only has to work on FreeBSD, there's nothing to gain in terms of effort. You have to write some rules for installation, be it in your local Makefile or in your port's Makefile.



Okay, I've written a non-port-specific `make install`. Now I want to test the port installer. It still requires a commit-push-update-the-Makefile cycle – the whole part I'm trying to speed up.

However...



T-Daemon said:


> I understand this as follows: You want to fetch your applications source code, _compressed in a file_, from a local directory.
> 
> You can define that location with the `MASTER_SITES` variable, followed by the file protocol (`file:///`) in the Makefile. That protocol is supported by fetch(1).



This is super helpful. I can zip up my src dir and stick it in the file repo. This is a lot cleaner than sticking a zip in $DISTFILES directly, and working out the dir structure that it expects.

Here's the script that does the job. Thanks!


----------



## zirias@ (May 16, 2022)

Still the thing is: You don't test a _port_ with every development cycle of your ("upstream") software. And my question remains: why do you want to do that? Your life will be a lot easier if you separate these concerns


----------



## patmaddox (May 16, 2022)

Zirias said:


> Still the thing is: You don't test a _port_ with every development cycle of your ("upstream") software. And my question remains: why do you want to do that? Your life will be a lot easier if you separate these concerns


You're right, I don't want to test a port with every development cycle. I want to test the port whenever I make changes to the installation process.

Most of the time, I'm just doing `mix test` and I'm good. But occasionally, I need to do something that uses some aspect of the installation process. That's what want to test without a commit & push cycle.

Which I have now accomplished!

Here's a concrete example for you: I want to configure some aspect of my app with an env var. So I add a call to `System.fetch_env!("MY_APP_FOO")` in my code. I test it out, works great.

Now I need to add that env var to the RC file. I add `MYAPP_FOO` to the RC file.

Without being able to quickly check the installation process, I'll push my code out, then update the Makefile with the new SHA, run it... and find that I have mis-matched the env var names.

I'm not doing my port install after every commit. But when I am working on the FreeBSD installation process, it's helpful to be able to run it against the current version of my source.

I _do_ see the value in having `make install` at the application level, even if it just copies files to $DESTDIR in the exact structure that I want for FreeBSD. So thank you for pointing that out


----------



## zirias@ (May 16, 2022)

I have to admit, rc files are a problem for this clear separation. BTDT. Trying to deliver such a file in your "upstream" tarball is kind of moot, as every system uses some different framework, from dreaded systemd to sysvrc to (awesome!) mewburn rc on FreeBSD  

But you'll have to admit this is an edge case


----------



## patmaddox (Oct 29, 2022)

For anyone else interested in this, I finally came up with a really clean solution:


```
make WRKSRC=$(pwd) USE_GITHUB=no DISTFILES="" -C port
```

It assumes a really simple USE_GITHUB Makefile (e.g. below).

Here’s the example repo: https://github.com/patmaddox/simple_port_example


```
PORTNAME= simple-port-example
CATEGORIES= devel
DISTVERSION= 1.0.0
DISTVERSIONPREFIX= v

MAINTAINER= pat@patmaddox.com

USE_GITHUB= yes
GH_ACCOUNT= patmaddox
GH_PROJECT= simple_port_example

.include <bsd.port.mk>
```


----------



## Alain De Vos (Oct 29, 2022)

Strange that you have & "USE_GITHUB=no" & "USE_GITHUB=yes"


----------



## patmaddox (Oct 29, 2022)

Not really. It's not any different from setting any other variable when running make.

USE_GITHUB=yes is for the production makefile.

But when I'm working on it locally, I want to use the local source directory. I do that by specifying WRKSRC. You're not allowed to set WRKSRC with USE_GITHUB=yes, so for _local development_ I override USE_GITHUB=no so it uses the local source instead of fetching from GitHub.


----------

