Difference between revisions of "Writing Makefiles"

From Apertium
Jump to navigation Jump to search
(works)
Line 68: Line 68:
 
-rm -rf .deps modes
 
-rm -rf .deps modes
 
</pre>
 
</pre>
  +
  +
==Avoid duplicating install="yes" (modes.xml) in Makefile.am==
  +
Most language pairs have lines like
  +
<pre>
  +
$(INSTALL_DATA) $(PREFIX1).mode $(DESTDIR)$(apertium_modesdir)
  +
rm $(PREFIX1).mode
  +
</pre>
  +
for every mode that has install="yes" in modes.xml. So setting install="yes" doesn't actually install the mode, it just creates an installable mode. This is redundant and confusing.
  +
  +
To actually install all and only those with install="yes", you can do this:
  +
<pre>
  +
install-data-local:
  +
mv modes modes.bak
  +
apertium-gen-modes modes.xml $(BASENAME)
  +
rm -rf modes
  +
mv modes.bak modes
  +
test -d $(DESTDIR)$(apertium_modesdir) || mkdir $(DESTDIR)$(apertium_modesdir)
  +
modes=`xmllint --xpath '//mode[@install="yes"]/@name' modes.xml | sed 's/ *name="\([^"]*\)"/\1.mode /g'`; \
  +
$(INSTALL_DATA) $$modes $(DESTDIR)$(apertium_nn_modesdir); \
  +
rm $$modes
  +
</pre>
  +
  +
(xmllint and sed are prerequisites of apertium/lttoolbox, and the sed line is portable, so it doesn't add any dependencies.)

Revision as of 08:13, 24 October 2013

Some tips for writing clean Makefile.am's in Apertium:

Only one modes goal

The command apertium-gen-modes creates _all_ modes files at once. So one goal is sufficient (and several can lead to problems with e.g. make -j2) when creating your debug modes.

So, make one goal for a mode you know it creates, and then put that goal into noinst_DATA:

modes/$(PREFIX1).mode: modes.xml
	apertium-validate-modes modes.xml
	apertium-gen-modes modes.xml
	cp *.mode modes/

noinst_DATA=modes/$(PREFIX1).mode

(noinst_DATA is for listing stuff that you want built, but not installed or distributed. Don't list the mode in TARGETS_COMMON or EXTRA_DIST, debug modes should not be installed or distributed.)

Avoid ending up with root-owned modes files

When you do "sudo make install", some Makefiles.am still have a bug where they'll leave around root-owned modes files. Then on the next "make", you see a "Permission denied" error, since it tries to create the same filenames again as non-root. (The installable mode files have paths pointing to e.g. /usr/local, while the non-install mode files have paths that point to your source development directory. You want the latter to hang around so you can do apertium -d . fie-bar.)

A workaround is this: when doing make install, first stash away any already existing development modes directory, the generate the install modes, then clean up the root-owned stuff and unstash the old modes directory:

install-data-local:
	mv modes modes.bak
	apertium-gen-modes modes.xml $(BASENAME)
	rm -rf modes
	mv modes.bak modes
	test -d $(DESTDIR)$(apertium_modesdir) || mkdir $(DESTDIR)$(apertium_modesdir)
	$(INSTALL_DATA) $(PREFIX1).mode $(DESTDIR)$(apertium_modesdir)
	$(INSTALL_DATA) $(PREFIX2).mode $(DESTDIR)$(apertium_modesdir)
	rm $(PREFIX1).mode $(PREFIX2).mode

This method leaves no root-owned files hanging around.

(It might look odd that we "rm -rf modes" there after gen-modes, but "apertium-gen-modes somedir" creates both apertium-foo-bar/foo-bar.mode and non-installable debug modes in apertium-foo-bar/modes/ – perhaps tihs could be fixed in apertium-gen-modes so we can avoid this makefile workaround.)

Use .deps/.d to say that the .deps directory must be created

Say you have several goals that put temporary files in .deps/, e.g.

.deps/apertium-wat-lol.lol.dix: apertium-wat-lol.lol.dix
	test -d .deps || mkdir .deps
	xsltproc lexchoicebil.xsl $< >$@

and so on. The .deps directory has to be created for the file in .deps to be created. If you put mkdir .deps in each such goal, you can get a race condition where two goals try to make .deps at the same time.

The solution is this: if a goal needs the .deps directory to be created, let it depend on the file .deps/.d. First put this in Makefile.am:

.deps/.d:
	test -d .deps || mkdir .deps
	touch $@

.PRECIOUS: .deps/.d

And then, instead of creating the dir in each goal, just depend on .deps/.d for those goals:

.deps/apertium-wat-lol.lol.dix: apertium-wat-lol.lol.dix .deps/.d
	xsltproc lexchoicebil.xsl $< >$@

(The PRECIOUS line prevents the .d file from being cleaned up and removed automatically.)

Removing directories on make clean

Say you want to remove .deps and modes on "make clean". Don't do CLEANFILES=-rf .deps modes file1 file2 …, it doesn't work everywhere.

A more portable solution is this:

CLEANFILES = $(TARGETS_COMMON)
clean-local: 
	-rm -rf .deps modes

Avoid duplicating install="yes" (modes.xml) in Makefile.am

Most language pairs have lines like

	$(INSTALL_DATA) $(PREFIX1).mode $(DESTDIR)$(apertium_modesdir)
	rm $(PREFIX1).mode

for every mode that has install="yes" in modes.xml. So setting install="yes" doesn't actually install the mode, it just creates an installable mode. This is redundant and confusing.

To actually install all and only those with install="yes", you can do this:

install-data-local:
	mv modes modes.bak
	apertium-gen-modes modes.xml $(BASENAME)
	rm -rf modes
	mv modes.bak modes
	test -d $(DESTDIR)$(apertium_modesdir) || mkdir $(DESTDIR)$(apertium_modesdir)
	modes=`xmllint --xpath '//mode[@install="yes"]/@name' modes.xml | sed 's/ *name="\([^"]*\)"/\1.mode /g'`; \
		$(INSTALL_DATA) $$modes $(DESTDIR)$(apertium_nn_modesdir); \
		rm $$modes

(xmllint and sed are prerequisites of apertium/lttoolbox, and the sed line is portable, so it doesn't add any dependencies.)