Skip to content

Make the autogenerated LIBS variable optionally-set#81

Open
pzel wants to merge 1 commit intosmlsharp:masterfrom
pzel:optional-lib-make-var
Open

Make the autogenerated LIBS variable optionally-set#81
pzel wants to merge 1 commit intosmlsharp:masterfrom
pzel:optional-lib-make-var

Conversation

@pzel
Copy link
Copy Markdown

@pzel pzel commented Oct 8, 2023

This PR modifies the Makefile generated by smlsharp -Mmm so that the LIBS env var is set to the empty string only if it isn't already present in the environment.

This allows for setting the LIBS variable on a higher level than in the Makefiles autogenerated by smlsharp -Mmm, therefore relieving the programmer from having to manually (re)edit them upon (re)generation.

Rationale

The rationale for this change is as follows. As far as I understand, smlsharp leverages the mechanisms in POSIX make to ensure that particular .sml files are compiled in the dependency order that is defined by their corresponding .smi files.

When working on a codebase that often changes, it's convenient to treat the Makefile as generated by smlsharp -Mmm as a transient file, and drive the project's compilation process from a top-level Makefile (or some other scripting/build system). This is especially the case when we also want to support compiling our own C files via make.

Now, let's say we're compiling a local.so shared library in the top-level Makefile. This entails

  1. Setting up the top-level Makefile to compile local.c to local.so 2) Manually editing the autogenerated Makefile and changing the line
LIBS =

to

LIBS = local.so

Now, whenever we change the smlsharp side of the project and wish to regenerate the smlsharp Makefile, we have to either a) always remember to go back and hand-edit the LIBS variable assignment; or b) write some kind of sed script to substitute the line in the autogenerated Makefile.

If the autogenerated Makefile used the conditional assignment operator ?=, we would only have to modify our top-level Makefile if we made changes to our C libraries, whereas the lower-level Makefile autogenerated by smlsharp could be safely regenerated as often as necessary without manual intervention.

This change modifies the LIBS = line to LIBS ?= in order to support this.

Example

Below is an example project that showcases the situation

% ls
libsqr.c  main.smi  main.sml  Makefile

% tail -n100 ./*
==> ./libsqr.c <==
short sqrShort (short n) { return (n * n); }
double sqrDouble (double n) { return (n * n); }

==> ./main.smi <==
_require "basis.smi";

==> ./main.sml <==
val sqrShort = _import "sqrShort" : int16 -> int16
val _ = print (Int16.toString (sqrShort 16) ^ "\n")

==> ./Makefile <==
LIBS=libsqr.so

all0: libsqr.so Makefile.smlsharp all

libsqr.so: libsqr.c
	gcc -shared -fPIC $< -o $@

Makefile.smlsharp: $(shell find . | grep '.smi$$')
	smlsharp -MMm main.smi > $@

include Makefile.smlsharp

The autogenerated Makefile.smlsharp gets included into our top-level project Makefile, and should inherit the LIBS variable we set up on the first line of the top-level Makefile.

We can simply call make to have:

  1. all our C libraries compiled and linked before any smlsharp code 2) the linked libraries defined in one place only
  2. Makefile.smlsharp regenerated if anything has changed in our smlsharp dependency tree
% make && ./main
smlsharp -MMm main.smi > Makefile.smlsharp
gcc -shared -fPIC libsqr.c -o libsqr.so
smlsharp -O2 -o main.o -c main.sml
smlsharp  -o main main.smi libsqr.so
256

And if we change something on the smlsharp side of things, the same command works without having to manually re-edit Makefile.smlsharp

% echo '_require "reify.smi"' >> ./main.smi
% echo 'val _ = Dynamic.pp "All done"' >> ./main.sml
% make
smlsharp -MMm main.smi > Makefile.smlsharp
smlsharp -O2 -o main.o -c main.sml
smlsharp  -o main main.smi libsqr.so
% ./main
256
"All done"

This PR modifies the Makefile generated by `smlsharp -Mmm` so that the `LIBS`
env var is set to the empty string only if it isn't already present in the
environment.

This allows for setting the LIBS variable on a higher level than in the
Makefiles autogenerated by `smlsharp -Mmm`, therefore relieving the programmer
from having to manually (re)edit them upon (re)generation.

Rationale
---------

The rationale for this change is as follows. As far as I understand, `smlsharp`
leverages the mechanisms in POSIX make to ensure that particular `.sml` files
are compiled in the dependency order that is defined by their corresponding
`.smi` files.

When working on a codebase that often changes, it's convenient to treat the
`Makefile` as generated by `smlsharp -Mmm` as a transient file, and drive the
project's compilation process from a top-level `Makefile` (or some other
scripting/build system). This is especially the case when we also want to
support compiling our own C files via `make`.

Now, let's say we're compiling a `local.so` shared library in the top-level
Makefile. This entails

1) Setting up the top-level Makefile to compile `local.c` to `local.so`
2) Manually editing the autogenerated Makefile and changing the line

```
LIBS =
```

to

```
LIBS = local.so
```

Now, whenever we change the smlsharp side of the project and wish to regenerate
the smlsharp Makefile, we have to either a) always remember to go back and
hand-edit the `LIBS` variable assignment; or b) write some kind of sed script
to substitute the line in the autogenerated Makefile.

If the autogenerated Makefile used the conditional assignment operator `?=`, we
would only have to modify our top-level Makefile if we made changes to our C
libraries, whereas the lower-level Makefile autogenerated by smlsharp could be
safely regenerated as often as necessary without manual intervention.

This change modifies the `LIBS = ` line to `LIBS ?= ` in order to support this.

Example
-------

Below is an example project that showcases the situation

```
% ls
libsqr.c  main.smi  main.sml  Makefile

% tail -n100 ./*
==> ./libsqr.c <==
short sqrShort (short n) { return (n * n); }
double sqrDouble (double n) { return (n * n); }

==> ./main.smi <==
_require "basis.smi";

==> ./main.sml <==
val sqrShort = _import "sqrShort" : int16 -> int16
val _ = print (Int16.toString (sqrShort 16) ^ "\n")

==> ./Makefile <==
LIBS=libsqr.so

all0: libsqr.so Makefile.smlsharp all

libsqr.so: libsqr.c
	gcc -shared -fPIC $< -o $@

Makefile.smlsharp: $(shell find . | grep '.smi$$')
	smlsharp -MMm main.smi > $@

include Makefile.smlsharp

```

The autogenerated `Makefile.smlsharp` gets included into our top-level project
Makefile, and should inherit the `LIBS` variable we set up on the first line of
the top-level Makefile.

We can simply call `make` to have:

1) all our C libraries compiled and linked before any smlsharp code
2) the linked libraries defined in one place only
3) `Makefile.smlsharp` regenerated if anything has changes in our smlsharp
dependency tree

```
% make && ./main
smlsharp -MMm main.smi > Makefile.smlsharp
gcc -shared -fPIC libsqr.c -o libsqr.so
smlsharp -O2 -o main.o -c main.sml
smlsharp  -o main main.smi libsqr.so
256
```

And if we change something on the smlsharp side of things, the same command
works without having to manually re-edit `Makefile.smlsharp`

```
% echo '_require "reify.smi"' >> ./main.smi
% echo 'val _ = Dynamic.pp "All done"' >> ./main.sml
% make
smlsharp -MMm main.smi > Makefile.smlsharp
smlsharp -O2 -o main.o -c main.sml
smlsharp  -o main main.smi libsqr.so
% ./main
256
"All done"
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant