Difference between revisions of "Writing Makefiles"
|  (works) | |||
| (11 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
| Some tips for writing clean Makefile.am's in Apertium: | Some tips for writing clean Makefile.am's in Apertium: | ||
| {{TOCD}} | |||
| ==Use apertium-init== | |||
| If you're creating a new monolingual module or pair, use [[apertium-init]]. It creates modules that Just Work. If you've got a problem with an old makefile, throw it all away and create a new one using [[apertium-init]]. | |||
| ==Only one modes goal== | |||
| The command <code>apertium-gen-modes</code> 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. | |||
| If you really want to debug your old makefile, read on for some tips. | |||
| So, make one goal for a mode you know it creates, and then put that goal into noinst_DATA: | |||
| ==Modes== | |||
| If you have <code>apertium</code> version 3.3 or higher, you can do this to simplify dealing with [[Modes]] files / modes.xml: | |||
| In <code>configure.ac</code>, add this: | |||
| <pre>AP_MKINCLUDE</pre> | |||
| In <code>Makefile.am</code>, add this: | |||
| <pre> | <pre> | ||
| # Only include one mode file here, the rest will be built along with it (listing several leads to problems with parallell make): | |||
| 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=modes/$(PREFIX1).mode | ||
| </pre> | |||
| (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.) | |||
| @ap_include@ | |||
| ==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 <code>apertium -d . fie-bar</code>.) | |||
| # Most language pairs don't need to specify anything else for install-data-local: | |||
| 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: | |||
| ⚫ | |||
| <pre> | |||
| ⚫ | |||
| EXTRA_DIST:  | |||
| 	mv modes modes.bak | |||
| 	modes.xml \ | |||
| 	# here you'll typically also have other things, like .dix and .t1x  | |||
| 	rm -rf modes | |||
|         # files that are to be included when you make a release | |||
| 	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 | |||
| </pre> | </pre> | ||
| This method leaves no root-owned files hanging around.  | |||
| ''Nowhere else should modes be mentioned in the <code>Makefile.am</code>.'' | |||
| (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.) | |||
| If you follow this system, the modes with install="yes" in modes.xml will be installed, and you won't end up with root-owned modes files, and make -j4 will work fine. | |||
| ==Use .deps/.d to say that the .deps directory must be created== | ==Use .deps/.d to say that the .deps directory must be created== | ||
| Line 51: | Line 50: | ||
| .PRECIOUS: .deps/.d | .PRECIOUS: .deps/.d | ||
| </pre> | </pre> | ||
| (If using apertium 3.3 or higher, you can just do AP_MKINCLUDE, which includes the above .deps/.d goal.) | |||
| And then, instead of creating the dir in each goal, just depend on .deps/.d for those goals: | And then, instead of creating the dir in each goal, just depend on .deps/.d for those goals: | ||
| <pre> | <pre> | ||
| Line 60: | Line 61: | ||
| ==Removing directories on make clean== | ==Removing directories on make clean== | ||
| Say you want to remove .deps and modes on "make clean". Don't do < | Say you want to remove .deps and modes on "make clean". Don't do <code>CLEANFILES=-rf .deps modes file1 file2 …</code>, it doesn't work everywhere.  | ||
| A more portable solution is this: | A more portable solution is this: | ||
| Line 69: | Line 70: | ||
| </pre> | </pre> | ||
| ==Where does this $AP_SRC1 stuff come from?== | |||
| ==Avoid duplicating install="yes" (modes.xml) in Makefile.am== | |||
| When you do <code>./autogen.sh --with-lang1=foo</code>, the variable AP_SRC1 will be set to "foo" in the Makefile. Your configure.ac should say something like <code>AP_CHECK_LING([1], [apertium-lol])</code> for that option to work. | |||
| 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> | |||
| ;The details: The autogen.sh script passes its arguments on to the configure script. If configure.ac says e.g. <code>AP_CHECK_LING([1], [apertium-lol])</code>, then the configure script will have a --with-lang1 option to set to the location of the package apertium-lol. If you don't pass --with-lang1 to autogen.sh, configure will attempt to find the location of an ''installed'' apertium-lol package using pkg-config.  | |||
| (xmllint and sed are prerequisites of apertium/lttoolbox, and the sed line is portable, so it doesn't add any dependencies.) | |||
| :;The gory details: The actual configure code for all this checking is in apertium.m4, typically in /usr/share/aclocal/apertium.m4 or /usr/local/share/aclocal/apertium.m4 if you're very interested in reading m4sh code. | |||
| [[Category:Tools]] | |||
Latest revision as of 13:09, 18 February 2015
Some tips for writing clean Makefile.am's in Apertium:
Use apertium-init[edit]
If you're creating a new monolingual module or pair, use apertium-init. It creates modules that Just Work. If you've got a problem with an old makefile, throw it all away and create a new one using apertium-init.
If you really want to debug your old makefile, read on for some tips.
Modes[edit]
If you have apertium version 3.3 or higher, you can do this to simplify dealing with Modes files / modes.xml:
In configure.ac, add this:
AP_MKINCLUDE
In Makefile.am, add this:
# Only include one mode file here, the rest will be built along with it (listing several leads to problems with parallell make):
noinst_DATA=modes/$(PREFIX1).mode
@ap_include@
# Most language pairs don't need to specify anything else for install-data-local:
install-data-local: install-modes
EXTRA_DIST: 
	modes.xml \
	# here you'll typically also have other things, like .dix and .t1x 
        # files that are to be included when you make a release
Nowhere else should modes be mentioned in the Makefile.am.
If you follow this system, the modes with install="yes" in modes.xml will be installed, and you won't end up with root-owned modes files, and make -j4 will work fine.
Use .deps/.d to say that the .deps directory must be created[edit]
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
(If using apertium 3.3 or higher, you can just do AP_MKINCLUDE, which includes the above .deps/.d goal.)
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[edit]
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
Where does this $AP_SRC1 stuff come from?[edit]
When you do ./autogen.sh --with-lang1=foo, the variable AP_SRC1 will be set to "foo" in the Makefile. Your configure.ac should say something like AP_CHECK_LING([1], [apertium-lol]) for that option to work.
- The details
- The autogen.sh script passes its arguments on to the configure script. If configure.ac says e.g. AP_CHECK_LING([1], [apertium-lol]), then the configure script will have a --with-lang1 option to set to the location of the package apertium-lol. If you don't pass --with-lang1 to autogen.sh, configure will attempt to find the location of an installed apertium-lol package using pkg-config.
- The gory details
- The actual configure code for all this checking is in apertium.m4, typically in /usr/share/aclocal/apertium.m4 or /usr/local/share/aclocal/apertium.m4 if you're very interested in reading m4sh code.
 

