lairworks / nas2d-core

NAS2D is an open source, object oriented 2D game development framework written in portable C++.
http://nas2d.lairworks.com
zlib License
10 stars 5 forks source link

Improve `makefile` project semantics #1129

Closed DanRStevens closed 1 year ago

DanRStevens commented 1 year ago

The current makefile doesn't really clearly define projects. It has targets for the different projects, though each project is mostly a duplicate block of similar code. It may make sense to extract out the common aspects of a project to help de-duplicate the makefile, and to make it easier to add new projects.

In particular, if the makefile could be made more generic, it can be re-used to simplify downstream projects such as OPHD, which may be split into multiple projects (https://github.com/OutpostUniverse/OPHD/issues/1191, https://github.com/OutpostUniverse/OPHD/issues/903), and so could make use of a high level concept of a project.


There is an existing makefile in the op2ext project which may be of use:

The problem with makefile-generic.mk is that it's reliant of GNU make, as opposed to Posix make, and our current MacOS build relies on the native make found on MacOS, which is Posix make.

The specific error seems to be with the use of define, for creating multiline variables, which doesn't seem to be supported with Posix make:

define DefineCppProject =

line 137: Need an operator


It may be possible to do something similar to the above makefile-generic.mk, but avoiding the use of define. Perhaps using more individual variables and foreach to iterate over them could be an alternate solution.

Some possibly relevant sections of the GNU make manual may provide some inspiration:

The section on the eval function is particularly interesting. It gives an example using all of the above, including define, and ties it all together with:

$(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog))))
DanRStevens commented 1 year ago

Was thinking it might be interesting to define a list of projects, with some config data, and then include a generic makefile that would setup dependency and build rules for them. The included makefile could automatically create target and dependency information based on variables, rather than only providing methods to be called to setup the projects.

One thought for that was to scan for variables with a given name pattern, which might be used to configure project settings. It seems GNU make allows for that possibility using the built-in .VARIABLES variable: 6.14 Other Special Variables

Unfortunately, .VARIABLES is GNU make only, with no support for it from Posix make.

It would still be possible to use a fixed variable name to define a list of projects, and then project specific variables for any needed config. Example:

ProjectNames := nas2d test testGraphics

ProjectConfig_test_dependsOn := nas2d
ProjectConfig_testGraphics_dependsOn := nas2d

include makefile-generic.mk

Once the project names are known, config could be accessed using 6.3.2 Computed Variable Names, such as $(ProjectConfig_$(ProjectName)_dependsOn).

Will probably want to use simpler names for project specific config than in the above example.

Hopefully defaults can be set such that explicit config isn't generally required. Perhaps the type of output could be assumed based on the project name. For instance, if a project name starts or ends with "lib", then the output may default to a static library. If the project name starts or ends with "dll", then the output may default to a DLL (Windows) or Shared Object (Linux). Otherwise the project could default to output an executable file.

A slight complication is that some projects can be compiled as either a static library or a DLL. That would be a configuration issue though, and not something I've really addressed yet in the context of a makefile.

Compiler flags, such as CXXFLAGS should probably be used as a global default, with the ability to override if there is a project specific version of the flags defined.

DanRStevens commented 1 year ago

So the portability situation is perhaps a little worse than I initially expected. It seems Posix make doesn't support the foreach syntax offered by GNU make, and GNU Make doesn't support the .for syntax offered by bmake/pmake (BSD make).

There is a possible workaround using the shell, as illustrated on StackOverflow: Designing a portable makefile loop

run: $(PROGS)
        for p in $(PROGS); do ./$$p; done

Edit: Looks like other functions, such as call and eval are also not available with other variants of make.

DanRStevens commented 1 year ago

Given the different implementations of make, and the limitations of many of them, it doesn't look to be reasonable to dynamically generate full project config from minmal metadata while also maintaining portability across different platforms and the implementations of make that they provide. Given that, I think the recent improvements are about as good as can be expected for the moment, this this Issue is probably worth closing now.