Makepp
HomeFAQGalleryDownloadSourceForgeCPAN

Makepp Incompatibilities

Incompatibilities between makepp and GNU make

Makepp was designed to be as close as possible to GNU make (http://www.gnu.org/software/make/manual/make.html). Gnu autotools (http://www.gnu.org/software/automake/manual/automake.html), CMake (http://www.cmake.org/), Premake (http://industriousone.com/premake and see remark below) or handcrafted legacy build systems should be buildable with makepp. This is so you can either migrate projects effortlessly. Or if you don't want to enjoy all of makepp's advantages (e.g. so others can still build your project with GNU make) while you profit from the reliability advantage for your development.

However, because of the difference in philosophy, some of GNU make's or POSIX make's (http://pubs.opengroup.org/onlinepubs/009695399/utilities/make.html) features cannot be supported. A few have not been implemented because we haven't had time. Most of the differences from GNU make are quite technical and only rarely cause problems. Alas the workarounds for the short-comings of traditional make are becoming more and more complex, and are giving makepp a hard time.

In a nutshell, if it doesn't build out of the box, try:

makepp --no-warn makepp_simple_concatenation=1 makepp_percent_subdirs=1 \
    --build-check=target_newer --last-chance-rules --no-remake-makefiles

If that succeeds, you can try to eliminate those arguments one by one. But if that fails, try adding either one or both of:

    --defer-include
    --traditional-recursive-make

If that also fails, the build system needs some tweaking to cooperate with makepp. Even if some options described here make something buildable, it is still recommended to adapt things slightly, so they become compatible out of the box with both makes.


Forcing more POSIX or GNU make compatibility

Here are some command line possibilities for getting many legacy build systems to work without modification. They cause makepp to emulate GNU make's behavior precisely.

Compatibility via the option: --build-check=target_newer

By default, makepp will attempt to rebuild all targets if any of the dependencies have changed since the last build, or if the command has changed (see the makepp_build_check manpage for details). This is normally what you want. Sometimes, however, you don't want the target to be rebuilt if it has been modified apart from the control of makepp (e.g., by editing it, or by running a program manually to make the file). You can force makepp to use the traditional make algorithm, which only rebuilds if any of the targets are newer than the dependencies, by adding this option to the command line.

Compatibility via the option: --dont-build=config.status

There are packages which try to autoconfigure themselves, or do other things, which gmake ignores unless being asked to, like:

config.status : configure
    ./config.status --recheck
configure : configure.in aclocal.m4 
    autoconf

Most people don't even have autoconf installed, so conscientiously doing everything by the rules, as makepp does, will fail. This option prevents that, if you figure out what not to build.

Compatibility via the option: --last-chance-rules

Default rules (pattern rules with no pattern dependencies) are not normally supported. Makepp instantiates all rules based on the existing files, so that it is aware of every file that could be generated. Alas this way it does not know how to instantiate a pattern rule with no pattern dependency. The :last_chance mechanism partially remedies that. Where this is good enough for legacy makefiles, this option allows turning it on globally.

Compatibility via the option: --no-warn

This one doesn't improve the result. Makepp will give warning messages for many things which the traditional Unix make accepts without flinching. This is because there are better ways to do them with makepp. If these warnings annoy you, you can turn them off with this option.

Compatibility via the option: --hybrid-recursive-make

Recursive invocations of make are often considered to be an unsafe practice (see Better system for hierarchical builds in the makepp manpage for details), but they are extremely common in existing makefiles. Makepp supports recursive make for backward compatibility; for new makefiles, it is much better to use the load_makefile statement, or makepp's implicit makefile loading mechanism.

In order to be able to use repositories for variant builds, and to help make recursive invocations of make safer, makepp normally does not actually invoke itself recursively even if you tell it to. Instead, a subprocess communicates with the parent process, and the actual build is done by the parent process.

This works in most cases, but you may not invoke several makefiles from the same directory, e.g., the following will not work:

target: dependencies
    $(MAKE) -f other_makefile targets

In this case makepp notices it is loading a 2nd makefile and complains. With this option instead it will fall back to the traditional way of building from additional makefiles in a separate makepp process each.

Note: Technically loading several makefiles would be no problem, but they usually have the same phony target names. Keeping that apart would mean a complete redesign of makepp internals. However, this will work, but it is not equivalent:

target: dependencies
    cd subdir && $(MAKE) -f other_makefile targets

Compatibility via the option: --traditional-recursive-make

Sometimes the previous option is not enough, especially if the recursive invocations use contradictory options. Makepp uses only one set of global options, so a submake is not allowed to modify them, as that would also pertain to other makefiles.

Adding this option to the command line, has the following undesirable side effects:

Even with the --traditional-recursive-make option, the environment variables MAKEOVERRIDES and MFLAGS are not set up, and ignored, so makefiles that depend on those will not work.

A Premake generated Makefile is only a funny wrapper to a sub-make invocation in the same directory. If you have some project target XYZ it will have a line like

    @${MAKE} --no-print-directory -C . -f XYZ.make

In this case you can avoid the --traditional-recursive-make option by directly invoking makepp with that -f XYZ.make option.

Compatibility without the option: --jobs=n

Legacy makefiles will sometimes not list all dependencies, relying on the order of execution to make them in time. In this situation makepp may manage to call a rule before its dependencies have all been made. Then results may be better with less, or even no parallel execution.

Compatibility via the variable: makepp_simple_concatenation=1

Rc-style substitution is the default way makepp performs variable substitution into text strings because it very rarely breaks legacy makefiles and is often useful in new makefiles. However, it does introduce occasional incompatibilities in the substitution of variables not surrounded by spaces. For example,

INCLUDE_PREFIX := -I/some/include/dir -I
INCLUDES := $(INCLUDE_PREFIX)/other/include/dir

will set INCLUDES to -I/some/include/dir/other/include/dir -I/other/include/dir if rc-style substitution is enabled, whereas GNU make would set it to -I/some/include/dir -I/other/include/dir.

There is also an incompatibility in the handling of whitespace in a variable:

null :=
T := -o $(null)             # T contains -o followed by one space.
OUTFILE = $(T)outfile

will set OUTFILE to -ooutfile if rc-style substitution is enabled, whereas GNU make would set it to -o outfile.

Both of these incompatibilities are removed by setting the makepp_simple_concatenation variable. Note, however, that even with makepp_simple_concatenation, makepp still treats whitespace incompatibly in some situations:

T := -o # Don't delete this comment.

GNU make sets T to contain -o followed by a space, whereas makepp strips out the trailing space anyway. If you want the trailing space, you must set makepp_simple_concatenation and also set T using the technique involving a dummy variable such as null, as shown above.

Workaround option --defer-include

Makepp reads makefiles in a single pass, where GNU make is multipass. Hence this will not work, because GIT-VERSION-FILE must be built to process the include statement. But it depends on something that is unknown at this point. To solve this, the .PHONY declaration must be moved up before the include statement:

GIT-VERSION-FILE: FORCE
    @$(SHELL_PATH) ./GIT-VERSION-GEN
include GIT-VERSION-FILE
.PHONY: FORCE

This option internally reorders the include statements to the end of the makefile. That may in turn cause variables to have different values, in which case you should set the problematic ones on the command line.

Workaround option --no-remake-makefiles

Typical open source requires calling configure to create the makefiles. But then these makefiles can contain rules to remake the makefile, by calling some command. Makepp will happily comply and update it according to the rule. But sometimes this is harmful, so just skip it.

Compatibility via the variable: makepp_percent_subdirs=1

By default, % in a pattern rule does not match directories. This means that a rule like this:

%.o: %.c
    $(CC) $(CFLAGS) -c $(input) -o $(output)

will not be applied to files like ../shared/xyz.c. If you want it to match files in subdirectories too, then set the variable makepp_percent_subdirs=1 on the command line or near the beginning of a makefile.

Compatibility via the environment variable: $MAKEPP_IGNORE_OPTS

Sometimes legacy recursive invocations pass options that makepp doesn't understand. Hopefully the option is not important, but it prevents makepp from running. With this environment variable you can ask makepp to silently ignore certain options. The value shall be a space separated list of options, which can come in 4 variants:

--long=x

A long option that expects an argument. This fact must be declared through the equals sign, though the actual use may also separated by whitespace, either --long=bla or --long bla.

--long

A long option without an argument.

-sx

A short option that expects an argument. This fact must be declared by adding something directly after the option, though the actual use may also separated by whitespace, either -sbla or -s bla.

-s

A short option without an argument.

E.g. override makepp's -R option by one without an argument and accept gmake's debug option with an argument:

export MAKEPP_IGNORE_OPTS='-R --debug=x'


Incompatibilities that require Makefile changes

Incompatibilities in order of expression expansion

$(MAKE) may include spaces

In an uninstalled makepp or if the platform doesn't seem to support starting a Perl script by magic number or with --traditional-recursive-make this variable will include at least one space. That is not a problem when using it as a command. But when passing it as an unquoted parameter to a script (as the Perl 5.14.0 build system does), it will tear it apart into separate parameters, leading to confusion. So as a parameter it is safer to quote it as '$(MAKE)'. which doesn't break backward compatibility.

Target-specific assignments don't propagate

Makepp's target-specific variables are slightly different from GNU make's in that they only apply to the rule for the one file mentioned, and not to any of its predecessors; see Target-specific assignments.

Parentheses or braces don't nest

Makepp ends expressions at the first matching parenthesis or brace. Instead of this

$(somefunction ... ( ) ...) # Gnu make style

you must use either of these

${somefunction ... ( ) ...} # Gnu make compatible
$((somefunction ... ( ) ...)) # Makepp extension

This will probably be fixed in version 2.1, maybe optionally.

Minor points

Pattern dependencies don't match phony targets
%.a: %.b; ...
$(phony x.b): ; ...         # does not provide a way to build x.a
Comments don't have continuation lines
# This is \
NOT a 2-line comment


Command line incompatibilities

Makepp supports a few of make's more useful command line options. The following, however, are not supported:

-d or --debug
-f -

Makepp's internal makefile objects are linked to file objects, so it can't handle stdin.

-i
-l or --load-average or --max-load
-m

Makepp's -m option has to do with signature method selection, whereas GNU make ignores -m.

-p or --print-data-base
-q or --question
-R or --no-builtin-variables

Makepp's -R option actually does something completely different.

-S --no-keep-going or --stop

The --stop option stops (puts to sleep) makepp after learning all the rules, so you can continue editing.

-t or --touch
-w or --print-directory

This happens automatically.

--warn-undefined-variables

Some of these can be easily supported if anyone cares.


Last modified: 2012-03-19