On W32 (and when cross-compiling for W32 from a linux system) I'd like to have automake build two executables from the same Makefile.am that differ in their extension.
E.g.
- foo.exe
- foo.com
Since automake magically handles the extension, i don't really see how to achieve that. E.g.
bin_PROGRAMS=foo foo.com
foo_SOURCES=foo.c
foo_com_SOURCES=foo.c
Will happily produce two binaries foo.exe and foo.com.exe :-(
The best I've come up so far with is:
AUTOMAKE_OPTIONS = no-exeext
bin_PROGRAMS=foo
noinst_PROGRAMS=foocom
bin_SCRIPTS=foo.com
foo_SOURCES=foo.c flub.c
foocom_SOURCES=foo.c knark.c
clean-local:
-rm -rf foo.com
foo.com: foocom$(EXEEXT)
cp $^ $#
Could I expect more?
Background
I'm building a cross-platform application (let's call it foo), with an automake-based buildsystem.
On un*x-like systems, the application stays in the foreground (and outputs to stdout/stderr).
On W32 this is usually unwanted (as it would require the application to be a Console Application with an ugly console-window to stay open while the application is running). Sometimes it is wanted though (e.g. for debugging).
The solution currently applied (with a non-automake based build-system on W32) is to build the same application both as foo.exe (a native W32 application), and foo.com (a Console Application).
I would basically use the best you have come up with with a few quite small changes:
AM_INSTALLCHECK_STD_OPTIONS_EXEMPT =
CLEANFILES =
bin_PROGRAMS =
noinst_PROGRAMS =
bin_SCRIPTS =
common_sources =
common_sources += foo.c
if BUILD_FOR_WINDOWS
AM_INSTALLCHECK_STD_OPTIONS_EXEMPT += foo$(EXEEXT)
bin_PROGRAMS += foo
foo_CFLAGS = -mwindows
foo_SOURCES = foo-windows.c
foo_SOURCES += $(common_sources)
AM_INSTALLCHECK_STD_OPTIONS_EXEMPT += foo-console$(EXEEXT)
noinst_PROGRAMS += foo-console
foo_console_CFLAGS = -mconsole
foo_console_SOURCES = foo-console.c
foo_console_SOURCES += $(common_sources)
CLEANFILES += foo.com
AM_INSTALLCHECK_STD_OPTIONS_EXEMPT += foo.com
bin_SCRIPTS += foo.com
foo.com: foo-console$(EXEEXT)
cp $^ $#
endif
List of changes:
Removed AUTOMAKE_OPTIONS = no-exeext which appears unnecessary.
Use CLEANFILES instead of hooking into clean-local.
Reformat for easy patching by adding/removing lines.
Add an AM_CONDITIONAL to build these windows specific programs if and only if you are actually building for a windows target. (For a MacOSX or GNU/Linux target, you would need to write and build very different programs.)
# Add this to configure.ac
build_for_windows=no
case "${host_os}" in
cygwin*|mingw*)
build_for_windows=yes
;;
esac
AM_CONDITIONAL([BUILD_FOR_WINDOWS],
[test "x$build_for_windows" = "xyes"])
Related
Typical makefile.am, which works, would look like this for my project:
noinst_LTLIBRARIES = libwinsane.la
CLEANFILES = init.cpp
libwinsane_la_CXXFLAGS = \
-I$(top_srcdir)
libwinsane_la_SOURCES = \
init.cpp
noinst_HEADERS = \
init.h
the issue is that I need to also process resource files. I need to use something like this:
noinst_LTLIBRARIES = libwinsane.la
libwinsane.o: init.o manifest.o
ld -relocatable -o $# init.o manifest.o
manifest.o: manifest.rc utf8.xml
windres -o $# manifest.rc
clean:
rm -f init.o manifest.o libwinsane.o
While this last example is valid makefile by itself (except noinst_LIBRARIES), its obviously wrong syntax for automake. But maybe there is a way for automake to just accept it and produce library in the end somehow?
Off the top of my head, and without 100% verifying its consistency with https://www.gnu.org/software/automake/manual/html_node/Program-and-Library-Variables.html or testing it, something like this should work more or less:
CLEANFILES =
noinst_LTLIBRARIES = libwinsane.la
libwinsane_la_SOURCES =
libwinsane_la_SOURCES += init.cpp
libwinsane_la_SOURCES += init.h
libwinsane_la_CXXFLAGS = -I$(top_srcdir)
libwinsane_la_DEPENDENCIES = manifest.$(OBJEXT)
libwinsane_la_LIBADD = manifest.$(OBJEXT)
CLEANFILES += manifest.$(OBJEXT)
manifest.$(OBJEXT): manifest.rc utf8.xml
windres -o $# $(srcdir)/manifest.rc
A few additional remarks:
The windres recipe probably does not work for out of source builds if utf8.xml has not just been buitl in $(builddir) and is therefore located in $(srcdir). Adding something like -I $(srcdir) to the windres command line will probably fix this, but cannot verify that without a MWE.
You will want to make windres configurable and replace the hard coded windres call by $(WINDRES), made user configurable by AC_ARG_VAR and (if $WINDRES has not been set) AC_CHECK_TOOL, and probably protected with a check in configure.ac that $WINDRES actually works. Depending on your project you can then either AC_MSG_ERROR(...) in case $WINDRES does not work, or AM_CONDITIONAL([HAVE_WINDRES], ...) and then put the Makefile.am parts related to manifest.$(OBJEXT) inside a if HAVE_WINDRES / endif conditional. The actual windres command might be called something like x86_64-w64-mingw32-windres when cross-compiling.
Why do you have CLEANFILES += init.cpp? I have left that out, as you do not want make clean to delete your source files. Or does init.cpp belong to BUILT_SOURCES?
Update: Apparently, I have overlooked something for compiling and linking with libtool: cannot build libtool library from non-libtool objects - any workaround?
I'm often creating png files out of dot (graphviz format) files. The command to do so is the following:
$ dot my_graph.dot -o my_graph.png -Tpng
However, I would like to be able to have a shorter command format like $ make my_graph.dot to automatically generate my png file.
For the moment, I'm using a Makefile in which I've defined the following rule, but the recipe is only available in the directory containing the Makefile
%.eps: %.dot
dot $< -o $# -Teps
Is it possible to define custom implicit GNU Make recipes ? Which would allow the above recipe to be available system-wide
If not, what solution do you use to solve those kind of problem ?
Setup:
Fedora Linux with ZSH/Bash
You could define shell functions in your shell's startup files, e.g.
dotpng()
{
echo dot ${1%.dot}.dot -o ${1%.dot}.png -Tpng;
}
This function can be called like
dotpng my_graph.dot
or
dotpng my_graph
The code ${1%.dot}.dot strips .dot from the file name if present and appends it (again) to allow both my_graph.dot and my_graph as function argument.
Is it possible to define custom implicit GNU Make recipes ?
Not without modifying the source code of GNU Make.
If not, what solution do you use to solve those kind of problem ?
I wouldn't be a fan o modyfying the system globally, but you could do:
Create a file /usr/local/lib/make/myimplicitrules.make with the content
%.eps: %.dot
dot $< -o $# -Teps
Use include /usr/local/lib/make/myimplicitrules.make in your Makefile.
I would rather use a git submodule or similar to share common configuration between projects, rather than depending on global configuration. Depending on global environment will make your program hard to test and non-portable.
I would rather go with a shell function, something along:
mymake() {
make -f <(cat <<'EOF'
%.eps: %.dot
dot $< -o $# -Teps
EOF
) "$#"
}
mymake my_graph.dot
GNU Make lets you specify extra makefiles to read using the MAKEFILES
environment variable. Quoting from info '(make)MAKEFILES Variable':
the default goal is never taken from one of these makefiles (or any
makefile included by them) and it is not an error if the files listed
in 'MAKEFILES' are not found
if you are running 'make' without a specific makefile, a makefile
in 'MAKEFILES' can do useful things to help the built-in implicit
rules work better
As an example, with no makefile in the current directory and the
following .mk files in make's include path (e.g. via
MAKEFLAGS=--include-dir="$HOME"/.local/lib/make/) you can create
subdir gen/ and convert my_graph.dot or dot/my_graph.dot by
running:
MAKEFILES=dot.mk make gen/my_graph.png
To further save some typing it's tempting to add MAKEFILES=dot.mk
to a session environment but defining MAKEFILES in startup files
can make things completely nontransparent. For that reason I prefer
seeing MAKEFILES=… on the command line.
File: dot.mk
include common.mk
genDir ?= gen/
dotDir ?= dot/
dotFlags ?= $(if $(DEBUG),-v)
Tvariant ?= :cairo:cairo
vpath %.dot $(dotDir)
$(genDir)%.png $(genDir)%.svg $(genDir)%.eps : %.dot | $(genDir).
dot $(dotFlags) $< -o $# -T'$(patsubst .%,%,$(suffix $#))$(Tvariant)'
The included common.mk is where you'd store general definitions to
manage directory creation, diagnostics etc., e.g.
.PRECIOUS: %/. ## preempt 'unlink: ...: Is a directory'
%/. : ; $(if $(wildcard $#),,mkdir -p -- $(#D))
References:
?= = := … - info '(make)Reading Makefiles'
vpath - info '(make)Selective Search'
order-only prerequisites (e.g. | $(genDir).) - info '(make)Prerequisite Types'
.PRECIOUS - info '(make)Chained Rules'
I am new to building R packages so I need some help :) I am using Rcpp/arrayfire and want a line in my makevars file to detect the users R version. Currently I have it set in 4.0, but I anticipate users having different R versions.
If this question has been answered, I apologize for not finding one!
Here are my global variables in the makevars file
R_VERSION = 4.0
AF_CXXFLAGS = -I/opt/arrayfire/include
AF_LIBS = -L/opt/arrayfire/lib -laf -Wl,-rpath,/opt/arrayfire/lib /Library/Frameworks/R.framework/Versions/$(R_VERSION)/Resources/library/RcppArrayFire/libs/RcppArrayFire.so -Wl,-rpath,/Library/Frameworks/R.framework/Versions/$(R_VERSION)/Resources/library/RcppArrayFire/libs
The usual workflow is to use a script called configure (which can be written in any language) which 'detects this' and then writes or alters src/Makevars accordingly.
If you know a little about make or want to learn it you can also do in a Makefile -- and our script src/Makevars is one. So something like this saved in a file Makefile
RVER = `Rscript -e 'cat(R.Version()$$major)'`
SOMEDIR = "/opt/foo/bar/"${RVER}"/some/more"
all:
#echo Using ${SOMEDIR}
results in
$ make
Using /opt/foo/bar/4/some/more
$
Edit And if you wanted just "4.2" out of the version, one way might be
> gsub("(\\.\\d)?$", "", format(getRversion()))
[1] "4.2"
>
Edit 2 As a full Makefile it becomes
#RVER = `Rscript -e 'cat(R.Version()$$major)'`
RVER = `Rscript -e 'cat(gsub("(\\\\.\\\\d)?$$", "", format(getRversion())))'`
SOMEDIR = "/opt/foo/bar/"${RVER}"/some/more"
all:
#echo Using ${SOMEDIR}
I'm writing a makefile that has to be compatible with both LINUX and the HP-UX operating system. I'm aware that certain shell commands in LINUX are not compatible with HP-UX. Consequently, I was wondering if it was possible to have macros declared conditionally so that if it was determined that the OS was HP-UX, the macro would be defined a certain way and if the OS was LINUX, it would be defined differently?
OS = `uname`
myOS = Linux
ifeq ($(OS),$(myOS))
message = "HELLO LINUX"
else
message = "HELLO FOO"
endif
all: install
install:
echo $(message)
I've tried using the approach above; however, it seems that ifeq determines that OS and myOS are not the same. They should both be 'Linux', but it's outputting the else block instead.
You shall use $(shell ...) in order to execute a SHELL command, this will work
OS := $(shell uname)
myOS := Linux
ifeq ($(OS),$(myOS))
message := "HELLO LINUX"
else
message := "HELLO FOO"
endif
all: install
install:
echo $(message)
Yes, you can define conditionals in makefiles.
This example taken from the above link
libs_for_gcc = -lgnu
normal_libs =
foo: $(objects)
ifeq ($(CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif
This shows the syntax for conditionals.
Given this defining anything specific should not be a problem. E.g., one could define/pass on marcos via the -D switch for a C program.
Update: To fix your problem with the OS variable not getting the output of the shell command uname you need to use shell function (as correctly pointed out by #AraundF): To quote from the link I posted:
"The shell function performs the same function that backquotes (``)
perform in most shells ..."
so you were on the right track.
What we used to do here is we define an environment variable ARCH on all systems we build stuff on, on a Linux system it will have value linux, on AIX aix, etc., in the Makefile we have:
include make.$(ARCH)
and for each platform we create a file called make.linux, make.aix, etc., with definitions specific for that platform, for example make.linux contains:
CC=g++
and make.aix contains
CC=xlC_r
This is quite a nice and clean approach, but nevertheless we are migrating to cmake ( http://www.cmake.org/ ) now.
I'm using a compiler for TI DSPs, so the default CC and LINK and AS tools make no sense. Below is an SConstruct file that works for me, I'm wondering if anyone has suggestions to make it better. Some problems:
I'd like to somehow tell it that my .obj files should go in a different directory than the source .c files. (it needs to know where, in order to figure out the SOURCES for the link step, and the dependencies for compile/linking) It would be nice to tie this in with the "-fr" and "-fs" arguments to the compiler, but I don't mind doing that manually.
There are some stock C files in the SConstruct file below, all start with a prefix of DSP2804x_. Right now scons can't figure out the dependencies for these, because I guess it's expecting the .obj files to live in the same directory, whereas my use of "-fr" and "-fs" for the compiler means those .obj files end up in the same directory as the SConstruct file. Is there a better way to do this? I'm guessing I should probably have a build step that copies these reference files into a local directory: if I change them, I want the changes to propagate to all projects that use them.
sigh....
env = Environment(
CC = 'C:/appl/ti/ccs/3.3/C2000/cgtools/bin/cl2000',
CCCOM = '$CC $CFLAGS $CCFLAGS $SOURCES',
CCFLAGS = Split('-g -q -pdr -d"_DEBUG" -d"LARGE_MODEL" -ml -mt -v28'),
LINKCOM = '$LINK $LINKFLAGS ${SOURCES.file} -o ${TARGET.base}.out',
LINK = 'C:/appl/ti/ccs/3.3/C2000/cgtools/bin/cl2000',
LINKFLAGS = Split('-z -q -c -ecode_start -stack0x200 -w -x'),
ASCOM = '$CC $CFLAGS $CCFLAGS $SOURCES',
#Bizarre but true. assembly is just like compiling C.
);
includes = {'CCFLAGS' : [
'-i../common/headers/include',
'-i../common/include',
'-fr.',
'-fs.'
]};
env.MergeFlags(includes);
links = {'LINKFLAGS' : [
'-m./Debug/Example_2804xGpioToggle.map',
'-i../common/headers/include',
'-iC:/appl/ti/ccs/3.3/C2000/xdais/lib',
'-iC:/appl/ti/ccs/3.3/C2000/cgtools/lib',
'-lrts2800_ml.lib',
'../common/cmd/28044_RAM_lnk.cmd',
'../common/headers/cmd/DSP2804x_Headers_nonBIOS.cmd'
]};
env.MergeFlags(links);
print "CCCOM is:", env['CCCOM'], "\n", env['LINKCOM'], '\n', env['ASCOM'];
env.Program('blink_gpio', [
'Example_2804xGpioToggle.c',
'../common/headers/source/DSP2804x_GlobalVariableDefs.c',
'../common/source/DSP2804x_CodeStartBranch.asm',
'../common/source/DSP2804x_DefaultIsr.c',
'../common/source/DSP2804x_PieCtrl.c',
'../common/source/DSP2804x_PieVect.c',
'../common/source/DSP2804x_SysCtrl.c'
]);
I solved both problems by doing a hierarchical build and using -fr=${TARGET.dir} in my compiler flags.