2018-08-07 15:19:03 +00:00
# The MCS makefiles
Author: Peter Williams < peter @newton .cx >
2014-08-13 10:39:27 +01:00
The new makefiles try to abstract building on Windows and Linux. They
try to provide a consistent set of ways to express the things that our
build system needs to let us do, specifically:
2018-08-07 15:19:03 +00:00
* Build recursively
* Build libraries and executables easily
* Let developers use different runtimes and class libaries
* Make distributions easily
* Provide a framework for testing
* Build platform-independently whenever possible
* Generate, update, and build monodoc documentation.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Makefile structure
==================
2014-08-13 10:39:27 +01:00
A general makefile looks like this:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
thisdir = class/Mono.My.Library
SUBDIRS =
include ../../build/rules.make
all-local:
do some stuff
install-local:
$(MKINSTALLDIRS) $(DESTDIR)$(prefix)/share/
$(INSTALL_DATA) myfile.txt $(DESTDIR)$(prefix)/share/myfiledir
clean-local:
rm -f my-generated-file
test-local: my_test_program.exe
run-test-local:
$(RUNTIME) my_test_program.exe
run-test-ondotnet-local:
$(RUNTIME) my_test_program.exe
DISTFILES = myfile.txt my_test_source.cs
dist-local: dist-default
doc-update-local:
my_test_program.exe: my_test_source.cs
$(CSCOMPILE) /target:exe /out:$@ $<
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
Each makefile follows the same pattern: it does some setup, includes
the standard make rules, and provides rules for eight standard targets:
2018-08-07 15:19:03 +00:00
`all` , `install` , `test` , `run-test` , `clean` , `dist` , and `doc-update` .
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
"Some setup" is defining two variables: `$(thisdir)` and
`$(SUBDIRS)` . `$(thisdir)` is the directory that the makefile lives in,
relative to the top directory (ie, `class/corlib` ) and `$(SUBDIRS)`
2014-08-13 10:39:27 +01:00
defines the subdirectories that should be built in.
The eight targets do the following:
2018-08-07 15:19:03 +00:00
* `all-local` builds whatever someone would expect to be built
when they just type `make'` Most likely `Foo.dll` or `Foo.exe`
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `install-local` installs whatever got built by `all-local` .
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `test-local` _builds_ the test programs or libraries but does
_not_ run them.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `run-test-local` actually runs the tests. It shouldn't
necessarily exit in an error if the test fails, but should make that
situation obvious. It should only run tests that take care of
themselves automatically; interactive tests should have an individual
target. The idea is that `make run-test` from the toplevel should be
able to proceed unsupervised and test everything that can be tested in
such a manner.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `run-xunit-test-local` this is a variation of the above, but it runs the
tests that were built with Xunit instead of NUnit.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `run-test-ondotnet-local` is a variant of `run-test-local` . It is used only to validate if our tests themselves works fine under Microsoft runtime (on Windows). Basically, in this target, we should not use $(TEST_RUNTIME) to test our libraries.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `clean-local` removes built files; `make clean` should leave
only files that go into a distribution tarball. (But it is not
necessarily true that all files that go into a tarball need to be left
after a make clean.)
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `dist-local` copies files into the distribution tree, which is
given by the variable `$(distdir)` . `dist-local` always depends on the
target `dist-default` . See ** `make dist` below.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `doc-update-local` should generate or update monodoc documentation,
if appropriate. This is usually only appropriate for libraries. It's
defined as a standard target so that it can easily be run recursively
across all libraries within the module.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Build configuration
===================
2014-08-13 10:39:27 +01:00
In general, MCS needs to be able to build relying only on the
2018-08-07 15:19:03 +00:00
existence of a runtime and core libraries (`mscorlib` , `System` ,
`System.Xml` ). So there shouldn't be any checking for libraries or
2014-08-13 10:39:27 +01:00
whatnot; MCS should be able to build out of the box. We try to keep
platform detection and feature testing (ie, for HP/UX echo) inside
the makefiles; right now, there's no configuration script, and it'd
be nice to keep it that way. (I am told that some people build on
both Windows and Linux in the same tree, which would be impossible to
do if we cached platform-related configury values.)
That being said, it's very convenient for developers to be able to
customize their builds to suit their needs. To allow this, the
Makefile rules are set up to allow people to override pretty much any
important variable.
2018-08-07 15:19:03 +00:00
Configuration variables are given defaults in `config-default.make` ;
`rules.make` optionally includes `$(topdir)/build/config.make` , so you
2014-08-13 10:39:27 +01:00
can customize your build without CVS trying to commit your modified
2018-08-07 15:19:03 +00:00
`config-default.make` all the time. Platform-specific variables are
defined in `$(topdir)/build/platforms/$(BUILD_PLATFORM).make` , where
`$(BUILD_PLATFORM)` is detected in `config-default.make` . (Currently, the only
choices are `linux.make` and `win32.make` )
2014-08-13 10:39:27 +01:00
The best way to learn what the configuration variables are is to read
2018-08-07 15:19:03 +00:00
`config.make` and `platform.make` . There aren't too many and hopefully
2014-08-13 10:39:27 +01:00
they should be self-explanatory; see the numerous examples below for
more information if you're confused.
2018-08-07 15:19:03 +00:00
Recommendations for platform specifics
--------------------------------------
2014-08-13 10:39:27 +01:00
If you find yourself needing a platform-specific customization, try
and express it in terms of a feature, rather than a platform test. In
other words, this is good:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
run-test-local: my-test.exe
ifdef PLATFORM_NEEDS_CRAZY_CRAP
crazy-crap
endif
$(RUNTIME) my-test.exe
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
and this is bad:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
run-test-local: my-test.exe
ifdef WINDOWS
crazy-crap
else
ifdef AMIGA
crazy-crap
endif
endif
$(RUNTIME) my-test.exe
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
The latter accumulates and gets unpleasant and it sucks. Granted,
right now we only have two platforms, so it's not a big deal, but it's
good form to get used to and practice. Anyway, take a look at how we
do the various corlib building hacks for examples of how we've done
platform-specificity. It certainly isn't pretty, but at least it's a
little structured.
2018-08-07 15:19:03 +00:00
Saving effort
=============
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
The point of the build system is to abstract things and take
2014-08-13 10:39:27 +01:00
care of all the easy stuff. So if you find yourself writing a
Makefile, know that there's probably already infrastructure to do what
2018-08-07 15:19:03 +00:00
you want. Here are all the common cases I can think of.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Compiling C# code? use:
-----------------------
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
my-program.exe: my-source.cs
$(CSCOMPILE) /target:exe /out:$@ $^
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
or
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
my-lib.dll: my-source.cs
$(CSCOMPILE) /target:library /out:$@ $^
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Note the `$@` and `$^` variables. The former means "the name of the
2014-08-13 10:39:27 +01:00
file that I am trying to make" and the latter means "all the
dependencies of the file I am trying to make." USE THESE VARIABLES
AGGRESSIVELY. Say that you add a new source to your program:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
my-program.exe: my-source.cs my-new-source.cs
$(CSCOMPILE) /target:exe /out:$@ $^
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
Because of the $^ variable, you don't need to remember to add another
file to the command line. Similarly, if you rename your program, you
won't need to remember to change the rule:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
MonoVaporizer.exe: my-source.cs my-new-source.cs
$(CSCOMPILE) /target:exe /out:$@ $^
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
will still work. Another useful variable is $< , which means "the first
dependency of whatever I'm building." If you order your dependencies
carefully it can be extremely useful.
2018-08-07 15:19:03 +00:00
Just building an executable?
----------------------------
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Then use:
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
PROGRAM = myprogram.exe
LOCAL_MCS_FLAGS = /r:System.Xml.dll
include ../build/executable.make
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
executable.make builds a program in the current directory. Its name is
2018-08-07 15:19:03 +00:00
held in `$(PROGRAM)` , and its sources are listed in the file
`$(PROGRAM).sources` . It might seem to make more sense to just list the
2014-08-13 10:39:27 +01:00
program's sources in the Makefile, but when we build on Windows we
need to change slashes around, which is much easier to do if the
2018-08-07 15:19:03 +00:00
sources are listed in a file. The variable `$(LOCAL_MCS_FLAGS)` changes
the flags given to the compiler; it is included in `$(CSCOMPILE)` so you
2014-08-13 10:39:27 +01:00
don't need to worry about it.
2018-08-07 15:19:03 +00:00
`executable.make` does a lot for you: it builds the program in 'make
all-local', installs the program in `$(prefix)/bin` , distributes the
2014-08-13 10:39:27 +01:00
sources, and defines empty test targets. Now, if your program has a
2018-08-07 15:19:03 +00:00
test, set the variable `HAS_TEST` :
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
PROGRAM = myprogram.exe
LOCAL_MCS_FLAGS = /r:System.Xml.dll
HAS_TEST = yes
include ../build/executable.make
test-local: mytester.exe
run-test-local: mytester.exe
$(RUNTIME) $<
mytester.exe: mytester.cs
$(CSCOMPILE) /target:exe /out:$@ mytester.cs
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
If your program has NUnit tests, set the variable `HAS_NUNIT_TEST` :
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
PROGRAM = myprogram.exe
LOCAL_MCS_FLAGS = /r:System.Xml.dll
HAS_NUNIT_TEST = yes
include ../build/executable.make
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
`HAS_NUNIT_TEST` tests follow `library.make` NUnit test conventions:
the files should be in a subdirectory called `Test/` , and if
your program is called `myprogram.exe` , they should be listed in
`myprogram_test.dll.sources` . The names in that files should *not* have
the `Test/` prefix. `make test'` will build `myprogram_test_$(PROFILE).dll`
2014-08-13 10:39:27 +01:00
in the current directory, automatically supplying the flags to
2018-08-07 15:19:03 +00:00
reference the original program and `NUnit.Framework.dll` .
2014-08-13 10:39:27 +01:00
If your program has 'built sources', that is, source files generated
from other files (say, generated by jay), define a variable called
2018-08-07 15:19:03 +00:00
`BUILT_SOURCES` and do *not* list the sources in `$(PROGRAM).sources` :
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
PROGRAM = myprogram.exe
LOCAL_MCS_FLAGS = /r:System.Xml.dll
BUILT_SOURCES = parser.cs
CLEAN_FILES = y.output
include ../build/executable.make
parser.cs: parser.jay
$(topdir)/jay/jay $< > $@
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
`executable.make` will automatically delete the `$(BUILT_SOURCES)` files
on `make clean` . Since this situation is a common occurrence and jay
2014-08-13 10:39:27 +01:00
happens to leave behind y.output files, you can also define a variable
2018-08-07 15:19:03 +00:00
called `$(CLEAN_FILES)` that lists extra files to be deleted when
`make clean` is called. (That's in addition to your executable and the built sources).
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Buildling a library? Use
------------------------
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
LIBRARY = Mono.MyLib.dll
LIB_MCS_FLAGS = /unsafe
TEST_MCS_FLAGS = /r:System.Xml.dll
include ../../build/library.make
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Where you library is called `$(LIBRARY)` ; it will be put into
`$(topdir)/class/lib` . `LIB_MCS_FLAGS` is the set of MCS flags to use when
2014-08-13 10:39:27 +01:00
compiling the library; in addition, a global set of flags called
2018-08-07 15:19:03 +00:00
`$(LIBRARY_FLAGS)` is added (that variable is defined in
`config-defaults.make` ), as well as the usual `$(LOCAL_MCS_FLAGS)` .
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
As in `executable.make` , the sources for your library are listed in
`$(LIBRARY).sources` . Note: these source lists should have Unix forward
2014-08-13 10:39:27 +01:00
slashes and Unix newlines (\n, not \r\n.) If you get an error about
"touch: need a filename", that means your .sources file doesn't end in
a newline. It should.
2018-08-07 15:19:03 +00:00
Now `library.make` also assumes that your library has an NUnit2 test
harness. The files should be in a subdirectory called `Test/` , and if
your library is called `Mono.Foo.dll` , they should be listed in
`Mono.Foo_test.dll.sources` . The names in that files should *not* have
the `Test/` prefix. `make test` will build `Mono.Foo_test.dll` in the
2014-08-13 10:39:27 +01:00
current directory, automatically supplying the flags to reference the
2018-08-07 15:19:03 +00:00
original library and `NUnit.Framework.dll` .
2014-08-13 10:39:27 +01:00
If you don't have a test, just do this:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
LIBRARY = Mono.MyLib.dll
LIB_MCS_FLAGS = /unsafe
NO_TEST = yes
include ../../build/library.make
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
and feel ashamed. Every good library has a test suite!
Extra flags needed to compile the test library should be listed in
$(TEST_MCS_FLAGS); often you will have a line like this:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
TEST_MCS_FLAGS = $(LIB_MCS_FLAGS)
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Again, `library.make` does a lot for you: it builds the dll, it
2014-08-13 10:39:27 +01:00
generates makefile fragments to track the dependencies, it installs
2018-08-07 15:19:03 +00:00
the library, it builds the test dll on `make test` , it runs
`$(TEST_HARNESS)` on it on `make run-test` , it removes the appropriate
2014-08-13 10:39:27 +01:00
files on 'make clean', and it distributes all the source files on
2018-08-07 15:19:03 +00:00
`make dist` . (`TEST_HARNESS` defaults to be `nunit-console.exe` but it may
be overridden to, say, `nunit-gtk` ). If you have extra files to
distribute when using either `library.make` or `executable.make` , use the
variable `$(EXTRA_DISTFILES)` :
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
EXTRA_DISTFILES = \
Test/testcase1.in \
Test/testcase1.out \
README
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Again, `library.make` and `executable.make` do the right things so that we
2014-08-13 10:39:27 +01:00
can build on Windows, doing some trickery to invert slashes and
overcome command-line length limitations. Use them unless you have a
really good reason not to. If you're building a bunch of small
2018-08-07 15:19:03 +00:00
executables, check out `tools/Makefile` or `tools/security/Makefile` ; if
2014-08-13 10:39:27 +01:00
all the files are in the current directory, changing slashes isn't a
big deal, and command-line lengths won't be a problem, so
executable.make isn't necessary (and indeed it won't work, since it
can only build one .exe in a directory).
2018-08-07 15:19:03 +00:00
If you're building a library, `library.make` is highly recommended; the
2014-08-13 10:39:27 +01:00
only DLL that doesn't use it is corlib, because building corlib is a
fair bit more complicated than it should be. Oh well.
2018-08-07 15:19:03 +00:00
`library.make` also automatically supports generating and updating
2014-08-13 10:39:27 +01:00
monodoc documentation. Documentation is stored within the
Documentation directory (a sibling to the Test directory), and is
generated/updated whenever the doc-update target is executed.
Assembling of the documentation so that the monodoc browser can
display the documentation is handled separately within the mcs/docs
all-local target; see mcs/docs/Makefile for details.
2018-08-07 15:19:03 +00:00
Running a C# program? Use $(RUNTIME)
------------------------------------
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
run-test-local: myprog.exe
$(RUNTIME) myprog.exe
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
`$(RUNTIME)` might be empty (if you're on windows), so don't expect to
2014-08-13 10:39:27 +01:00
be able to give it any arguments. If you're on a platform which has an
interpreter or jitter, $(RUNTIME_FLAGS) is included in $(RUNTIME), so
set that variable.
$(TEST_RUNTIME) is the runtime to use when running tests. Right now it's
just "mono --debug".
2018-08-07 15:19:03 +00:00
Calling the compiler directly? Use $(MCS).
------------------------------------------
2014-08-13 10:39:27 +01:00
Really, you should use $(CSCOMPILE) whenever possible, but $(MCS) is
out there. $(BOOTSTRAP_MCS) is the C# compiler that we use to build
mcs.exe; on Linux, we then use mcs.exe to build everything else, but
on Windows, we use csc.exe to build everything. Only use
$(BOOTSTRAP_MCS) if you know what you're doing.
2018-08-07 15:19:03 +00:00
Compiling C code? Use $(CCOMPILE)
---------------------------------
2014-08-13 10:39:27 +01:00
To give it flags, set $(LOCAL_CFLAGS). As with compiling C#, the
variable $(CFLAGS) will automatically be included on the command line.
2018-08-07 15:19:03 +00:00
Compiling resources with resgen
-------------------------------
2016-11-10 13:04:39 +00:00
If you have a resource that should be compiled with resgen and
included in your assembly, you can use the RESOURCES_DEFS variable.
This variable can contain lists of pairs that are separated by comma
to represent the resource ID as embedded in the assembly followed by
the file name, like this:
2014-08-13 10:39:27 +01:00
2016-11-10 13:04:39 +00:00
RESOURCE_DEFS = Messages,TextResources.resx Errors,ErrorList.txt
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Documentation-related needs? Use $(MDOC)
----------------------------------------
2014-08-13 10:39:27 +01:00
$(MDOC) is a front-end to the monodoc documentation system, supporting
documentation generation, updating, importing from Microsoft XML
Documentation and ECMA documentation formats, assembling documentation
for use within the monodoc documentation browser, and exporting
documentation to various other output formats such as static HTML.
It is currently only used for library.make's doc-update-local target
and for assembling documentation within $topdir/docs.
2018-08-07 15:19:03 +00:00
Installing files? Use $(MKINSTALLDIRS), $(INSTALL_DATA) or $(INSTALL_BIN), $(prefix), and $(DESTDIR).
-----------------------------------------------------------------------------------------------------
2014-08-13 10:39:27 +01:00
Every time a file is installed the commands should look like this:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
install-local:
$(MKINSTALLDIRS) $(DESTDIR)$(prefix)/my/dir
$(INSTALL_DATA) myfile $(DESTDIR)$(prefix)/my/dir
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
This way the directory is created recursively if needed (admittedly, we could
probably rely on mkdir -p), the file is given the correct permissions,
the user can override $(MKINSTALLDIRS) and $(INSTALL) if they need to,
and we can support $(DESTDIR) installs. We use $(DESTDIR) to make
monocharge tarballs, and it's useful otherwise, so try and use it
consistently.
2018-08-07 15:19:03 +00:00
'make dist'? Use $(DISTFILES)
-----------------------------
2014-08-13 10:39:27 +01:00
The 'dist-default' target will copy the files listed in $(DISTFILES)
into the distribution directory, as well as Makefile and ChangeLog if
they exist. This is almost always all that you need, so ideally your
make dist support should only be:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
DISTFILES = README Test/thoughts.txt
dist-local: dist-default
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
DISTFILES will cope correctly with files in subdirectories, by the
way. Note that if you put a nonexistant file or a directory in
DISTFILES it will *not* complain; it will just ignore it.
If you want to test your 'make dist' code, you can try
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
$ cd class/Mono.MyClass
$ make dist-local distdir=TEST
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
And your files should be copied into TEST/ in the current directory.
There is a toplevel 'make distcheck' target, which will build a dist
tarball, try to build it, install files to a temporary prefix, make
clean it, make a distribution, and compare the files left over to the
files originally in the tarball: they should be the same. But this
takes about 15 minutes to run on my 1.1 Ghz computer, so it's not for
the faint of heart.
2018-08-07 15:19:03 +00:00
Lots of files? Use $(wildcard *.foo)
------------------------------------
2014-08-13 10:39:27 +01:00
When specifying the sources to a library or executable, wildcards are
not encouraged; in fact they're not allowed if you use library.make or
executable.make. But there are times when they're useful, eg:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
DISTFILES = $(wildcard Test/*.in) $(wildcard Test/*.out)
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Just so you know that `make` has this feature.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Referencing files in other directories? Use $(topdir).
------------------------------------------------------
2014-08-13 10:39:27 +01:00
$(topdir) is the path to the top directory from the current build
directory. Basically it's a sequence of ../.. computed from the value
that you give $(thisdir) at the top of your Makefile. Try to reference
things from $(topdir), so your code can be moved or cut-and-pasted
around with a minimum of fuss.
2018-08-07 15:19:03 +00:00
Conditional building? Use ifdef/ifndef/endif
--------------------------------------------
2014-08-13 10:39:27 +01:00
Now in general we want to avoid conditional building, but sometimes
something doesn't work on Linux or already exists on Windows or
whatnot. (See below on recommended form for how to build
platform-specifically.) GNU Make supports the following construction:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
BUILD_EXPERIMENTAL = yes
ifdef BUILD_EXPERIMENTAL
experimental_stuff = my-experiment.exe
else
experimental_stuff =
endif
all-local: my-sane.exe $(experimental_stuff)
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
'ifdef' means 'if the variable is set to nonempty', so you could have
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
BUILD_EXPERIMENTAL = colorless green ideas sleep furiously
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
and Make would be happy. I hope that the meaning of 'ifndef' should be
obvious. If you want to only sometimes build a target, the above
construction is the recommended way to go about it; it's nice to have
the rules exist in a Makefile even if they aren't invoked.
If you want to see why conditionals aren't nice, take a look at
library.make or class/corlib/Makefile.
2018-08-07 15:19:03 +00:00
'Private' directories that shouldn't be built by default? Use DIST_ONLY_SUBDIRS
--------------------------------------------------------------------------------
2014-08-13 10:39:27 +01:00
Several of the MCS class libraries have demo or experimental
implementations that depend on things not included with MCS (say,
Gtk#). We don't want to build them by default, because the user might
not have those dependencies installed, but it's nice to have a
Makefile for them to be built nicely.
First of all, there's nothing stopping you from writing a Makefile for
such a directory; just don't put it in the SUBDIRS line of its parent
directory. That way, you can do all the normal build things like 'make
all' or 'make clean' in that directory, but people trying to bootstrap
their system won't run into problems.
At the same time you probably want to include this directory in the
distribution so that people can use your demo or experimental code if
they know what they're doing. Hence the variable
$(DIST_ONLY_SUBDIRS). As you might guess, it's like the SUBDIRS
variable: it lists subdirectories that a regular shouldn't recurse
into, but should have their 'make dist' rules invoked.
Say you've written Mono.MyFancyLib.dll and you have
a demo app using Gtk# called MyFancyDemo. The Makefile rules might
look like this:
class/Mono.MyFancyLib/Makefile
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
thisdir = class/Mono.MyFancyLib
SUBDIRS =
DIST_ONLY_SUBDIRS = MyFancyDemo
include ../../build/rules.make
LIBRARY = Mono.MyFancyLib.dll
LIB_MCS_FLAGS = /r:System.dll
TEST_MCS_FLAGS = $(LIB_MCS_FLAGS)
include ../../build/library.make
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
class/Mono.MyFancyLib/MyFancyDemo/Makefile
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
thisdir = class/Mono.MyFancyLib/MyFancyDemo
SUBDIRS =
include ../../../build/rules.make
PROGRAM = FancyDemo.exe
LOCAL_MCS_FLAGS = /r:gtk-sharp.dll
include ../../../build/executable.make
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Special recursion needs?
------------------------
2014-08-13 10:39:27 +01:00
By default, rules.make defines the all, install, clean, etc. targets
to look something like this:
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
all: all-recursive
$(MAKE) all-local
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
Sometimes that doesn't cut it; say for example you want to check for
something before doing a lengthy recursive build (see
$(topdir)/Makefile) or you have a something like this
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
class/MyLibrary:
Build MyLibrary.dll
class/MyLibrary/Test:
Build TestMyLibrary.exe
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
`make clean test` will fail here, because the build will happen in
the Test subdirectory first, so there will be no `MyLibrary.dll` to link
2014-08-13 10:39:27 +01:00
against. (Unless you write a nasty evil relative path rule which is
strongly discouraged.)
Anyway, to solve this problem you can do
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
thisdir = class/MyLibrary
SUBDIRS = Test
include ../../build/rules.make
# Normally, make runs 'all-recursive' first, and then 'all-local'
# With this, we ensure that 'all-local' is executed first.
all-recursive: all-local
test-recursive: test-local
...
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
A few implementation details
----------------------------
2014-08-13 10:39:27 +01:00
The way rules.make does its recursion is very standard; it maps
2018-08-07 15:19:03 +00:00
`{all,install,clean,dist,test}` to `$@-recursive` , which executes that rule
in each directory in `$(SUBDIRS)` , and then calls `$@-local` bin the current
2014-08-13 10:39:27 +01:00
directory. So something that gets built in a subdirectory cannot rely on
something that gets built in its parent directory. If this is a problem,
see the previous section. Note that the recursive rule for 'dist' is
different; it makes dist-recursive in subdirectories, so you at least
have to define that rule.
Note that even a directory that doesn't, for example, have any tests
must still define test-local; otherwise 'make test' run from the
toplevel directory will break.
2018-08-07 15:19:03 +00:00
Flags for Tools
---------------
2014-08-13 10:39:27 +01:00
We want to make it so that the user can specify certain flags to
always be given to a tool, so there's a general way of implementing
FLAGS variables:
2018-08-07 15:19:03 +00:00
* `$(foo_FLAGS)` remains unset or defaulted to something
sensible; the user can provide overrides this way.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `$(LOCAL_foo_FLAGS)` is set in a specific Makefile to
provide necessary values.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `$(PLATFORM_foo_FLAGS)` is set in the platform configuration
to provide platform-specific values.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `$(PROFILE_foo_FLAGS)` is set in the profile configuration
to provide profile-specific values.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
* `$(USE_foo_FLAGS)` is defined to be the combination of all of
the above, and it's what is actually passed to $(foo).
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
`$(MCS_FLAGS)` and `$(CFLAGS)` follow this model. If you end up finding
2014-08-13 10:39:27 +01:00
that another tool is used commonly (hm, jay...), please follow this form.
2018-08-07 15:19:03 +00:00
Portability tips
----------------
2014-08-13 10:39:27 +01:00
Always use the icky Windows /argument way of passing parameters to the C#
compiler so that csc can be used.
2018-08-07 15:19:03 +00:00
Always use `/r:foo.dll` , not `/r:foo` . Windows requires the former.
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
Use `/r:$(corlib)` , not `/r:corlib` .
2014-08-13 10:39:27 +01:00
If you're writing shell script code as part of a make rule, remember
that Windows has command-line length limits. So something like
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
mytool $(all_the_sources_to_corlib)
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
Is probably going to cause problems. As I understand it,
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
for f in $(all_the_sources_to_corlib) ; do ...
2018-08-07 15:19:03 +00:00
```
2014-08-13 10:39:27 +01:00
is ok, since the shell itself doesn't have those limitations. Other
than that, you should still try to write fairly portable shell
script. Linux and Cygwin both use the GNU utilities, but there's at
least one hardy soul trying to build Mono on HP/UX, and no doubt there
will be ports to more Unices as time goes on.
2018-08-07 15:19:03 +00:00
Misc
----
2014-08-13 10:39:27 +01:00
2018-08-07 15:19:03 +00:00
We still don't use `/d:NET_1_1` ; it causes some build problems right
2014-08-13 10:39:27 +01:00
now.
There's a hack in class/System.Data/Makefile to work around a very
strange crash in the runtime with some custom attribute stuff. It'd be
nice to fix it.
Also, there's a /lib:$(prefix)/lib in the System.dll Makefile, which
is for some reason necessary if System.Xml.dll hasn't been built yet.
(Well, it's necessary because of the /r:System.Xml.dll, but that
should be in the search path, it seems.)
A lot of the weird targets in the old makefiles have been dropped; I
have a feeling that a lot of them are archaic and not needed anymore.
I'd really like to write a build tool in C#. It would be nice to have
something really extensible and well-designed and clean. NAnt is,
IMHO, an apalling abomination and a tragically bad attempt at solving
the software building problem. Just so you know.
(On the other hand, NUnit is really neat.)
Peter