Automake: Run a script before traversing subdirectories - automake

I have a build system setup for a library using the GNU autotools. The library has subdirectories for source and header files. It also contains a script in the top directory which auto-generates a source and header file (not the same as the config file) in the appropriate subdirectories. It is necessary that these files are generated before make is performed in the subdirectories.
What is the simplest way to have the script run before subdirectories are traversed (i.e. when user calls make, the script is ran before traversing SUBDIRS)? I have tried adding rules like all-local with no success.
EDIT:
Top-level directory:
configure.ac Makefile.am src include myscript.sh
Makefile.am:
EXTRA_DIST = myscript.sh
ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} -I m4
SUBDIRS = src include
.PHONY: gen-script
gen-script:
./myscript.sh
src/Makefile.am:
AM_CPPFLAGS = -I$(top_srcdir)/include
lib_LTLIBRARIES = libmylib.la
libmylib_la_SOURCES = \
file1.cxx \
file2.cxx \
autogen-file.cxx
clean-local:
rm -f autogen-file.cxx
include/Makefile.am:
nobase_include_HEADERS = \
file1.h \
file2.h \
autogen-file.h
clean-local:
rm -f autogen-file.h

I think that the best solution would be to get rid of the recursive make and only have the top-level Makefile.am. Then you'd simply add a rule
include/autogen-file.h src/autogen-file.cxx: myscript.sh
${SHELL} $<
and list include/autogen-file.h in BUILT_SOURCES.
If you want to keep your recursive structure (even if this is considered harmful), you could place the rule to generate the files into the Makefile.ams in the sub-directories.
In src/Makefile.am:
autogen-file.cxx: ../myscript.sh
cd ${top_srcdir} && ${SHELL} myscript.sh
And in include/Makefile.am:
autogen-file.h: ../myscript.sh
cd ${top_srcdir} && ${SHELL} myscript.sh
By the way, how often do you need to re-build those? If the generated files only depend on the project configuration, you could simply run myscript.sh at the end of your configure.ac and delete the generated files only on make distclean.

What you can do is force the current directory to run before the rest of the subdirectories with
SUBDIRS = . src include
More seriously, though, you should not use recursive automake. Particularly if your structure is relatively simple as it seems to be. If you have interests in looking up how to do that you can see my documentation on Autotools Mythbuser.

Related

Check if sub directories are present in ./work when it is created inside makefile?

In my makefile, I want to check if sub directories are present in a given folder.
This is how I am doing it now..
setup:
mkdir -p ./work
DIR=$(shell find ./work -maxdepth 0 -type d -print)
check:
if [-z $(DIR) ]; then \
echo "null" ; \
else \
echo "present" ; \
fi;
I cannot create the ./work outside the makefile. So the problem is that since ./work is created inside the makefile, it gives me an error/warning that ./work is not present before actually executing the script.
Is there a way to check if sub directories are present in ./work when it is created inside makefile?
One possibility, which also is supportive of the make principle to build/create only things that your target is depending on, are order-only prerequisites. Now that you have heard this term order-only I want you from now on to mentally replace it with the much better fitting term "existence-only" (which is completely unofficial because I made it up) to gain an easier understanding. order-only prerequisites are written in the prerequisite list to the right side of a | pipe symbol and their meaning is: the target of this rule depends on the existence of this prerequisite but not on its timestamp. This is exactly what we want from a rule that needs to place something inside of a directory: if the directory exists, then we don't care about its date (mainly because in Unix directories get a new timestamp when a new file is created there) and we carry on with the rest of the targets dependencies. If however the directory doesn't exist, execute its rule first, which hopefully creates the directory:
.PHONY: all
all: foo
#cat subdir/subsubdir/subsubsubdir/foo.txt
subdir/subsubdir/subsubsubdir/foo.txt: | subdir/subsubdir/subsubsubdir
#echo This is file foo.txt > subdir/subsubdir/subsubsubdir/foo.txt
subdir:
mkdir subdir
subdir/subsubdir: | subdir
mkdir subdir/subsubdir
subdir/subsubdir/subsubsubdir: | subdir/subsubdir
mkdir subdir/subsubdir/subsubsubdir

run Cmake and make in parent directory

I have a project which is in parent directory a. The script runs the executables in 3 different subdirectories. Example below:
A
/ | \
B C D
Now, I would like to compile the cpp files in B, C, and D using a scrpit from A.
So far, in my script, I remove all the old CMakeCache.txt and Makefile files and the CMakeFiles directory to make sure there is no overlap.
THen I run cmake B/ followed by make -C B/. I do this for each subdirectory. But I get an error saying CMake Error: The source "/home/ybouvron/Documents/A/B/CMakeLists.txt" does not match the source "/home/ybouvron/Documents/A/C/CMakeLists.txt" used to generate cache. Re-run cmake with a different source directory.
Why am I getting this and how to I fix it? Seems like it's trying to compile the two as the same project, but in each of the CMakeLists.txt files in the subdirectories, they have different project names and executable names.
Thanks in advance.
#! /bin/bash
echo Deleting old make files
rm B/CMakeCache.txt
rm -r B/CMakeFiles/
rm B/Makefile
rm C/CMakeCache.txt
rm -r C/CMakeFiles/
rm C/Makefile
rm D/CMakeCache.txt
rm -r D/CMakeFiles/
rm D/Makefile
set -e
echo Compiling subsystems...
cmake B
make -C B
cmake C/
make -C C/
cmake D/
make -C D/
cmake B configures project in subdirectory B into current directory. make -C B builds project, which should be configured into subdirectory B. For in-source build of the project B you need cd B && cmake ., so make -C B will then build that project.
– Tsyvarev

Clean up unmentioned intermediate files in Make

I have a Makefile that builds a shapefile as an intermediate steps.
.INTERMEDIATE : senate_boundaries.shp
senate_boundaries.shp : Senate\ shape\ files.zip
unzip -j "$<"
A full shapefile comes with more than just a .shp, but also a .prj file, a .dbf file, and a bunch of others. These files are created when "Senate shape files.zip" is unzipped.
These other files are never an explicit target or dependency.
.INTERMEDIATE : senate_boundaries.prj senate_boundaries.dbf
does not seem to do anything.
How can I tell Make to clean up these other files?
You can add something like this to your recipe:
rm -f $(wildcard Senate\shape\*.prj)
But that will only work for that one file and you would have to manually add each extension to get rid of.
so something like this might do the trick:
rm -f $(shell ls Senate\shape\ | grep -v .shp&)
Another option is to unzip into a temp directory and then copy the file you want out and remove the the temp directory.

Makefile dynamic rules based on a file

I have a number of binary files (images, etc.). I need to copy some of them to an output directory as part of my build process.
The list of files that need to be copied is based on some rather complex logic, and it are generated dynamically by a Python script.
Suppose I have the following in deps.txt:
a.png
b.gif
c.mp4
How could I use a makefile to copy any necessary files to the output directory?
For example, if the output directoryalready included c.mp4 and an out-of-date version of b.gif, running the makefile would copy a.png and b.gif to the output directory (but not c.mp4).
The simplest way, if you're using GNU make, is to use an auto-generated include file, like this:
deps.txt.mk: deps.txt
cat $< | while read f; do echo "\$$(OUTPUT_DIR)/$$f: $$f ; cp $$< $$#"; done > $#
-include deps.txt.mk
If you're not using GNU make, you'll have to use recursion instead: have a rule that creates the makefile (like above), then run $(MAKE) -f deps.txt.mk to actually do the installation. Let me know if you need that example.

Using make to place message files in correct directory structure

I have an existing project where I am adding gettext support. Unfortunately, due to the project's structure and limitations, I cannot go through the recommended route of changing the project over to automake. Also unfortunately, my make-fu is pretty weak and I'm having troubles implementing rules to build my output archive:
Take all .po files in the msg subdir and run msgfmt on them to produce .mo files (in my target dir)
Put the .po files in the directory structure expected by gettext: (dir)/(locale)/LC_MESSAGES/(domainname).mo
Here's what I have so far
MSGSRC=msg/*.po
MSGOBJ=$(addprefix $(TARGET_BUILD_PATH)/$(target)/,$(MSG_SRC:.po=.mo))
$(TARGET_BUILD_PATH)/$(target)/msg/%.mo: msg/%.po
msgfmt -c $< -o $#
# Put in correct place
mkdir -p $(TARGET_BUILD_PATH)/$(target)/msg/$(*F)/LC_MESSAGES
cp $# $(TARGET_BUILD_PATH)/$(target)/msg/$(*F)/LC_MESSAGES/myapp.mo
archive: $(MSGOBJ) (other objs....)
(make the archive tarball...)
The problem with the existing code is that for some reason $(*F) comes out just * instead of the locale name (the .po files are named en_US.po, etc). It also seems incorrect because the target should be the real target, not the hacky msgfmt and copy I have. The directory structure is important because the makefile is run a few times for different cross-compiles ($(target)) and the output is archived into a tarball for installation on the devices.
I assume you can use GNU make.
First of all, let make expand the wildcards. This is important for later postprocessing:
MSGSRC=$(wildcard msg/*.po)
Now you should get lists of file names in MSGSRC and MSGOBJ. Additionally, the make manual marks $(F) as obsolete, and $ (the stem of the name) should contain just the locale. So,
mkdir -p $(TARGET_BUILD_PATH)/$(target)/msg/$*/LC_MESSAGES
should do the trick just fine, the same for the cp rule.
I do it slightly different. Here are my po files:
$ find msg -type f
msg/bg_BG.po
msg/de_DE.po
Here's the Makefile:
MSGLANGS=$(notdir $(wildcard msg/*po))
MSGOBJS=$(addprefix locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/appname.mo))
gettext: $(MSGOBJS)
locale/%.UTF-8/LC_MESSAGES/appname.mo: msg/%.po
mkdir -p $(dir $#)
msgfmt -c -o $# msg/$*.po
And these are the resulting mo files:
$ find locale -type f
locale/bg_BG.UTF-8/LC_MESSAGES/appname.mo
locale/de_DE.UTF-8/LC_MESSAGES/appname.mo

Resources