somake/README.md
2016-08-26 22:59:19 +02:00

12 KiB

This repository contains the Illumos make source code (in branch illumos) and based on that a port to Linux (branch master).

The Illumos make is based on Sun's dmake, which is compatible to Sun make.

Motivation: compile old Solaris projects (that use Sun make) under Linux without much effort. Think: the C code needs serious fixing because it includes metric tons of non-portable constructs such that you want to postpone the porting of the existing build system (which heavily uses Sun make specific extensions).

Another motivation to use a Sun compatible make might be the inclination to test one of its extensions, e.g. target groups, command and hidden dependencies.

2016, Georg Sauthofff mail@georg.so

Build Instructions

git clone https://github.com/gsauthof/somake.git
mkdir somake-build
cd somake-build
cmake ../somake -DCMAKE_BUILD_TYPE=Release
make

Or to use Ninja instead of GNU Make:

git clone https://github.com/gsauthof/somake.git
mkdir somake-build
cd somake-build
cmake ../somake -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja-build

Examples

The following assumes that the repository was cloned to the directory somake and that somake-build is the build directory.

Copy the example files and the basic rule file for testing:

$ cd ../somake-build
$ cp -r ../somake/example/ .
$ cp ../somake/bin/make.rules.file example/make.rules
$ cd example
$ ln -s ../somake

Compile the hello world program:

$ ./somake -m serial helloworld
cc    -o helloworld helloworld.c

Verify that make detects that no rebuild is necessary:

$ ./somake -m serial helloworld
`helloworld' is up to date.

Test command dependencies:

$ ./somake -m serial helloworld CC=gcc
gcc    -o helloworld helloworld.c

Also works for other variables:

$ ./somake -m serial helloworld CC=gcc CFLAGS=-Wall
gcc -Wall   -o helloworld helloworld.c

Test hidden dependency checks:

$ ./somake -m serial hello
cc   -c  hello.c
cc   -c  world.c
cc  hello.o world.o -o hello
$ ./somake -m serial hello
`hello' is up to date.
$ touch world.h
$ ./somake -m serial hello
cc   -c  hello.c
cc   -c  world.c
cc  hello.o world.o -o hello
$ ./somake -m serial hello
`hello' is up to date.
$ touch world.h
$ ./somake -m serial world.o
cc   -c  world.c
$ ./somake -m serial hello
cc   -c  hello.c
cc  hello.o world.o -o hello

Note that command and hidden dependencies are enabled by declaring the .KEEP_STATE: pseudo target.

Test target groups:

$ ./somake -m serial main_foo
./gen_foo.sh
cc   -c  main_foo.c
cc   -c  foo.c
cc  foo.h main_foo.o foo.o -o main_foo

Now remove the + in the generating rule

foo.c + foo.h: gen_foo.sh
    ./gen_foo.sh

and run again:

$ ./somake -m serial clean
$ rm .make.state
$ ./somake -m serial main_foo
./gen_foo.sh
cc   -c  main_foo.c
./gen_foo.sh
cc   -c  foo.c
cc  foo.h main_foo.o foo.o -o main_foo

Note that the ./gen_foo.sh action is now executed twice because the rule

foo.c foo.h: gen_foo.sh
    ./gen_foo.sh

is equivalent to two separate rules:

foo.c: gen_foo.sh
    ./gen_foo.sh

foo.h: gen_foo.sh
    ./gen_foo.sh

This is how classic make behaves - also GNU make - although in this toy example it is harder to trigger - because in sequential execution GNU make uses a advantageous timestamp comparison strategy:

$ make main_foo
./gen_foo.sh
cc    -c -o main_foo.o main_foo.c
cc    -c -o foo.o foo.c
cc  main_foo.o foo.o -o main_foo

But a parallel execution will do:

$ make -j3 main_foo
./gen_foo.sh
./gen_foo.sh
cc    -c -o main_foo.o main_foo.c
cc    -c -o foo.o foo.c
cc  main_foo.o foo.o -o main_foo

Note that GNU make supports target groups with pattern rules, though:

$ make -j3 both
echo x > foo.bar
sed 's/x/y/' foo.bar > foo.one
sed 's/x/z/' foo.bar > foo.two

With Sun make the same result is yielded iff a + is inserted between both pattern targets:

$ ./somake -m serial both
echo x > foo.bar
sed 's/x/y/' foo.bar > foo.one
sed 's/x/z/' foo.bar > foo.two

The example makefile also contains an example for Sun make style conditional macro assignments (with :=):

$ ./somake -m serial warn-helloworld
cc -Wall   -o helloworld helloworld.c

The combination with command dependencies makes this feature even more useful:

$ ./somake -m serial warn-helloworld
cc -Wall   -o helloworld helloworld.c
$ ./somake -m serial helloworld
cc    -o helloworld helloworld.c
$ ./somake -m serial warn-helloworld
cc -Wall   -o helloworld helloworld.c

In contrast, GNU make doesn't rebuild helloworld because it doesn't consider changed variables.

Background

Illumos is based on OpenSolaris, the now discontinued open-source version of Solaris. OpenSolaris was started by Sun after the Solaris 10 release and was closed down in 2010 after Oracle bought Sun. Sun used the CDDL license on its open sourced code.

Traditionally, Solaris includes it own version of make under /usr/ccs/bin/make, also known as 'Sun make'. It has some extensions over other make versions, such as BSD make and POSIX make. It only supports sequential execution (unlike e.g. GNU make), however, Sun also developed dmake (think: distributed make), usually bundled with the Solaris compiler suite 'Solaris Studio'. Dmake is compatible with the Sun make syntax and besides serial execution it supports parallel execution on an SMP system and on a cluster (apparently similar to distcc). It was also open-sourced by Sun under the CDDL.

Illumos started with the code base that included the original Sun make but later switched to dmake.

Comparison with other make Implementations

GNU make is the most popular, portable and thus relevant make. Similar to most GNU tools it contains many very useful extension over the traditional make and it basically runs everywhere.

Examples of GNU make features that are missing from Sun make:

  • parallel execution (the Sun make compatible dmake supports it)
  • conditional constructs
  • many built-in function
  • user-defined functions
  • templates
  • guile integration

Sun make features that aren't supported by GNU make:

  • target groups - i.e. to specify that more than one target depends on a source file and that the one action generates those targets at once. GNU make only supports a group of targets with pattern rules.
  • command dependencies - i.e. a target is also considered out-of-date if the command changes - i.e. due to changed values of variables like $(CC) or $(CFLAGS)
  • hidden dependencies - automatic discovery of header dependencies

There are also features where GNU and Sun make just use different syntax. For example, conditional macros:

  • Sun make: target := VAR = value
  • GNU make: taget: VAR = value

(Note that GNU make interprets := as assignment with immediate expansion.)

As always, neither GNU make nor Sun make/dmake are bug-free. For example, in versions released before 2016, the include file generation in GNU make prints misleading messages (cf. e.g. bug 102). Sun make's .make.state file (that is used for hidden dependency and command dependency tracking) sometimes causes more harm than good - e.g. when outdated dependencies aren't removed (e.g. when one converts a target group into a target list).

The distributed make that comes with OpenOffice is also named DMake. It seems that it was independently developed of Sun's dmake - and that it uses a different syntax.

Portability

The changes in the master branch fix portability issues in the original code such that the resulting make compiles and runs under Linux. Also, a CMake build file is included for portable building.

Although those changes target Linux, they aren't really Linux specific. That means that the ported make should also compile on other modern POSIXy operating systems.

So far, the port was successfully tested under:

  • Fedora 23/x86-64
  • Solaris 10/SPARC

Naming

I avoided the names dmake, imake and smake because there are already multiple published make versions that use those names. There is a dmake that comes with Solaris Studio, a dmake bundled with OpenOffice, a dmake that OpenOffice's dmake is based on, an imake to build old X versions and the Schily smake.

Thus, to not add to the confusion I chose somake.

Installation

The build file also contains an install target, e.g.:

$ DESTDIR=dest ninja-build install
[1/1] Install the project...
-- Install configuration: "Release"
-- Installing: dest/usr/local/bin/somake
[..]

If you use the standard makefile generator with cmake, just replace ninja-build with make. Also, omitting the DESTDIR installs everything for real. The destdir mechanism is useful for preparing a binary package and just to have a preview.

To change the default install prefix, you have to call cmake differently, e.g.:

$ cmake ../somake -DCMAKE_INSTALL_PREFIX=/usr \
    -DCMAKE_BUILD_TYPE=Release -G Ninja
$ DESTDIR=dest ninja-build install
[..]
-- Install configuration: "Release"
-- Installing: dest/usr/bin/somake
[..]

Tweaks

If you are compiling on a legacy platform with ancient libraries you may need to tweak the cmake call a little bit. For example, to compile with GCC on a Solaris 10 system:

$ CC=gcc CXX=g++ CFLAGS='-m64' CXXFLAGS='-m64' \
    cmake -DCMAKE_BUILD_TYPE=Release ../somake

Manual Installation

In case you prefer a manual installation, basically it is just:

  • copy the created somake binary to a bin/ directory under some prefix
  • copy the man page into the related manpath
  • copy the rule files that contain the built-in rules and are located in the bin/ subdirectory of this repository to one of the directories searched by somake (and also remove the .file suffix)

Rule Search Path

Using make.rules as an example, somake tries to open it in the following order:

  1. make.rules
  2. $ORIGIN/../share/somake/make.rules # added by this port
  3. $ORIGIN/../share/lib/make/make.rules
  4. $ORIGIN/../../share/make.rules
  5. /usr/share/lib/make/make.rules
  6. /etc/default/make.rules

Packaging

The CMake build file CMakeLists.txt also defines some variables for CPack, the CMake companion tool for creating binary packages.

For example, to create .rpm and .deb packages:

$ cmake ../somake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr \
    -DCPACK_GENERATOR='RPM;DEB' -GNinja
$ ninja-build package
$  ls somake-*
somake-0.6.0-Linux.deb  somake-0.6.0-Linux.rpm

As always, the -GNinja generator option can be dropped - ninja-build has to be replaced with make then.

The content of the archives can be verified like this:

$ dpkg --contents somake-0.6.0-Linux.deb
$ dpkg --info somake-0.6.0-Linux.deb

Or:

$ rpm2cpio somake-0.6.0-Linux.rpm | cpio --list -v
$ rpm -qip somake-0.6.0-Linux.rpm

License

Perhaps not required by the CDDL, but I license my changes also under the CDDL.